diff -r 537ddd8aa407 -r 2036ebfaccda jni/OSNetworkSystem.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jni/OSNetworkSystem.cpp Fri Nov 20 19:29:42 2009 +0100 @@ -0,0 +1,3688 @@ +/* + * Copyright (C) 2009 The Sipdroid Open Source Project + * Copyright (C) 2007 The Android Open Source Project + * + * This file is part of Sipdroid (http://www.sipdroid.org) + * + * Sipdroid is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this source code; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define LOG_TAG "OSNetworkSystem" + +#include +#include "jni.h" +#include "errno.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#include +//#include +//#include +//#include "AndroidSystemNatives.h" + +/** + * @name Socket Errors + * Error codes for socket operations + * + * @internal SOCKERR* range from -200 to -299 avoid overlap + */ +#define SOCKERR_BADSOCKET -200 /* generic error */ +#define SOCKERR_NOTINITIALIZED -201 /* socket library uninitialized */ +#define SOCKERR_BADAF -202 /* bad address family */ +#define SOCKERR_BADPROTO -203 /* bad protocol */ +#define SOCKERR_BADTYPE -204 /* bad type */ +#define SOCKERR_SYSTEMBUSY -205 /* system busy handling requests */ +#define SOCKERR_SYSTEMFULL -206 /* too many sockets */ +#define SOCKERR_NOTCONNECTED -207 /* socket is not connected */ +#define SOCKERR_INTERRUPTED -208 /* the call was cancelled */ +#define SOCKERR_TIMEOUT -209 /* the operation timed out */ +#define SOCKERR_CONNRESET -210 /* the connection was reset */ +#define SOCKERR_WOULDBLOCK -211 /* the socket is marked as nonblocking operation would block */ +#define SOCKERR_ADDRNOTAVAIL -212 /* address not available */ +#define SOCKERR_ADDRINUSE -213 /* address already in use */ +#define SOCKERR_NOTBOUND -214 /* the socket is not bound */ +#define SOCKERR_UNKNOWNSOCKET -215 /* resolution of fileDescriptor to socket failed */ +#define SOCKERR_INVALIDTIMEOUT -216 /* the specified timeout is invalid */ +#define SOCKERR_FDSETFULL -217 /* Unable to create an FDSET */ +#define SOCKERR_TIMEVALFULL -218 /* Unable to create a TIMEVAL */ +#define SOCKERR_REMSOCKSHUTDOWN -219 /* The remote socket has shutdown gracefully */ +#define SOCKERR_NOTLISTENING -220 /* listen() was not invoked prior to accept() */ +#define SOCKERR_NOTSTREAMSOCK -221 /* The socket does not support connection-oriented service */ +#define SOCKERR_ALREADYBOUND -222 /* The socket is already bound to an address */ +#define SOCKERR_NBWITHLINGER -223 /* The socket is marked non-blocking & SO_LINGER is non-zero */ +#define SOCKERR_ISCONNECTED -224 /* The socket is already connected */ +#define SOCKERR_NOBUFFERS -225 /* No buffer space is available */ +#define SOCKERR_HOSTNOTFOUND -226 /* Authoritative Answer Host not found */ +#define SOCKERR_NODATA -227 /* Valid name, no data record of requested type */ +#define SOCKERR_BOUNDORCONN -228 /* The socket has not been bound or is already connected */ +#define SOCKERR_OPNOTSUPP -229 /* The socket does not support the operation */ +#define SOCKERR_OPTUNSUPP -230 /* The socket option is not supported */ +#define SOCKERR_OPTARGSINVALID -231 /* The socket option arguments are invalid */ +#define SOCKERR_SOCKLEVELINVALID -232 /* The socket level is invalid */ +#define SOCKERR_TIMEOUTFAILURE -233 +#define SOCKERR_SOCKADDRALLOCFAIL -234 /* Unable to allocate the sockaddr structure */ +#define SOCKERR_FDSET_SIZEBAD -235 /* The calculated maximum size of the file descriptor set is bad */ +#define SOCKERR_UNKNOWNFLAG -236 /* The flag is unknown */ +#define SOCKERR_MSGSIZE -237 /* The datagram was too big to fit the specified buffer & was truncated. */ +#define SOCKERR_NORECOVERY -238 /* The operation failed with no recovery possible */ +#define SOCKERR_ARGSINVALID -239 /* The arguments are invalid */ +#define SOCKERR_BADDESC -240 /* The socket argument is not a valid file descriptor */ +#define SOCKERR_NOTSOCK -241 /* The socket argument is not a socket */ +#define SOCKERR_HOSTENTALLOCFAIL -242 /* Unable to allocate the hostent structure */ +#define SOCKERR_TIMEVALALLOCFAIL -243 /* Unable to allocate the timeval structure */ +#define SOCKERR_LINGERALLOCFAIL -244 /* Unable to allocate the linger structure */ +#define SOCKERR_IPMREQALLOCFAIL -245 /* Unable to allocate the ipmreq structure */ +#define SOCKERR_FDSETALLOCFAIL -246 /* Unable to allocate the fdset structure */ +#define SOCKERR_OPFAILED -247 /* Operation failed */ +#define SOCKERR_VALUE_NULL -248 /* The value indexed was NULL */ +#define SOCKERR_CONNECTION_REFUSED -249 /* connection was refused */ +#define SOCKERR_ENETUNREACH -250 /* network is not reachable */ +#define SOCKERR_EACCES -251 /* permissions do not allow action on socket */ +#define SOCKERR_EHOSTUNREACH -252 /* no route to host */ +#define SOCKERR_EPIPE -253 /* broken pipe */ + +#define JAVASOCKOPT_TCP_NODELAY 1 +#define JAVASOCKOPT_IP_TOS 3 +#define JAVASOCKOPT_SO_REUSEADDR 4 +#define JAVASOCKOPT_SO_KEEPALIVE 8 +#define JAVASOCKOPT_MCAST_TIME_TO_LIVE 10 /* Currently unused */ +#define JAVASOCKOPT_SO_BINDADDR 15 +#define JAVASOCKOPT_MCAST_INTERFACE 16 +#define JAVASOCKOPT_MCAST_TTL 17 +#define JAVASOCKOPT_IP_MULTICAST_LOOP 18 +#define JAVASOCKOPT_MCAST_ADD_MEMBERSHIP 19 +#define JAVASOCKOPT_MCAST_DROP_MEMBERSHIP 20 +#define JAVASOCKOPT_IP_MULTICAST_IF2 31 +#define JAVASOCKOPT_SO_BROADCAST 32 +#define JAVASOCKOPT_SO_LINGER 128 +#define JAVASOCKOPT_REUSEADDR_AND_REUSEPORT 10001 +#define JAVASOCKOPT_SO_SNDBUF 4097 +#define JAVASOCKOPT_SO_RCVBUF 4098 +#define JAVASOCKOPT_SO_RCVTIMEOUT 4102 +#define JAVASOCKOPT_SO_OOBINLINE 4099 + +/* constants for calling multi-call functions */ +#define SOCKET_STEP_START 10 +#define SOCKET_STEP_CHECK 20 +#define SOCKET_STEP_DONE 30 + +#define BROKEN_MULTICAST_IF 1 +#define BROKEN_MULTICAST_TTL 2 +#define BROKEN_TCP_NODELAY 4 + +#define SOCKET_CONNECT_STEP_START 0 +#define SOCKET_CONNECT_STEP_CHECK 1 + +#define SOCKET_OP_NONE 0 +#define SOCKET_OP_READ 1 +#define SOCKET_OP_WRITE 2 +#define SOCKET_READ_WRITE 3 + +#define SOCKET_MSG_PEEK 1 +#define SOCKET_MSG_OOB 2 + +#define SOCKET_NOFLAGS 0 + +#undef BUFFERSIZE +#define BUFFERSIZE 2048 + +// wait for 500000 usec = 0.5 second +#define SEND_RETRY_TIME 500000 + + +struct CachedFields { + jfieldID fd_descriptor; + jclass iaddr_class; + jmethodID iaddr_class_init; + jmethodID iaddr_getbyaddress; + jfieldID iaddr_ipaddress; + jclass genericipmreq_class; + jclass integer_class; + jmethodID integer_class_init; + jfieldID integer_class_value; + jclass boolean_class; + jmethodID boolean_class_init; + jfieldID boolean_class_value; + jclass byte_class; + jmethodID byte_class_init; + jfieldID byte_class_value; + jclass string_class; + jmethodID string_class_init; + jfieldID socketimpl_address; + jfieldID socketimpl_port; + jclass dpack_class; + jfieldID dpack_address; + jfieldID dpack_port; + jfieldID dpack_length; + jclass fd_class; + jfieldID descriptor; +} gCachedFields; + +static int useAdbNetworking = 0; + +/* needed for connecting with timeout */ +typedef struct selectFDSet { + int nfds; + int sock; + fd_set writeSet; + fd_set readSet; + fd_set exceptionSet; +} selectFDSet; + +static const char * netLookupErrorString(int anErrorNum); + +#define log_socket_close(a,b) +#define log_socket_connect(a,b,c) +#define add_send_stats(a,b) +#define add_recv_stats(a,b) +#define adb_networking_connect_fd(a,b) 0 +#define adb_networking_gethostbyname(a,b) 0 +#define PROPERTY_VALUE_MAX 1 +#define property_get(a,b,c) +#define assert(a) +/* + * Throw an exception with the specified class and an optional message. + */ +int jniThrowException(JNIEnv* env, const char* className, const char* msg) +{ + jclass exceptionClass; + + exceptionClass = env->FindClass(className); + if (exceptionClass == NULL) { +// LOGE("Unable to find exception class %s\n", className); + assert(0); /* fatal during dev; should always be fatal? */ + return -1; + } + + if (env->ThrowNew(exceptionClass, msg) != JNI_OK) { +// LOGE("Failed throwing '%s' '%s'\n", className, msg); + assert(!"failed to throw"); + } + return 0; +} + +/* + * Internal helper function. + * + * Get the file descriptor. + */ +static inline int getFd(JNIEnv* env, jobject obj) +{ + return env->GetIntField(obj, gCachedFields.descriptor); +} + +/* + * Internal helper function. + * + * Set the file descriptor. + */ +static inline void setFd(JNIEnv* env, jobject obj, jint value) +{ + env->SetIntField(obj, gCachedFields.descriptor, value); +} + +/* + * For JNIHelp.c + * Get an int file descriptor from a java.io.FileDescriptor + */ + +static int jniGetFDFromFileDescriptor (JNIEnv* env, jobject fileDescriptor) { + + return getFd(env, fileDescriptor); +} + +/* + * For JNIHelp.c + * Set the descriptor of a java.io.FileDescriptor + */ + +static void jniSetFileDescriptorOfFD (JNIEnv* env, jobject fileDescriptor, int value) { + + setFd(env, fileDescriptor, value); +} + +/** + * Throws an SocketException with the message affiliated with the errorCode. + */ +static void throwSocketException(JNIEnv *env, int errorCode) { + jniThrowException(env, "java/net/SocketException", + netLookupErrorString(errorCode)); +} + +/** + * Throws an IOException with the given message. + */ +static void throwIOExceptionStr(JNIEnv *env, const char *message) { + jniThrowException(env, "java/io/IOException", message); +} + +/** + * Throws a NullPointerException. + */ +static void throwNullPointerException(JNIEnv *env) { + jniThrowException(env, "java/lang/NullPointerException", NULL); +} + +/** + * Converts a 4-byte array to a native address structure. Throws a + * NullPointerException or an IOException in case of error. This is + * signaled by a return value of -1. The normal return value is 0. + */ +static int javaAddressToStructIn( + JNIEnv *env, jbyteArray java_address, struct in_addr *address) { + + memset(address, 0, sizeof(address)); + + if (java_address == NULL) { + return -1; + } + + if (env->GetArrayLength(java_address) != sizeof(address->s_addr)) { + return -1; + } + + jbyte * java_address_bytes + = env->GetByteArrayElements(java_address, NULL); + + memcpy(&(address->s_addr), + java_address_bytes, + sizeof(address->s_addr)); + + env->ReleaseByteArrayElements(java_address, java_address_bytes, JNI_ABORT); + + return 0; +} + +/** + * Converts a native address structure to a 4-byte array. Throws a + * NullPointerException or an IOException in case of error. This is + * signaled by a return value of -1. The normal return value is 0. + */ +static int structInToJavaAddress( + JNIEnv *env, struct in_addr *address, jbyteArray java_address) { + + if (java_address == NULL) { + return -1; + } + + if (env->GetArrayLength(java_address) != sizeof(address->s_addr)) { + return -1; + } + + jbyte *java_address_bytes; + + java_address_bytes = env->GetByteArrayElements(java_address, NULL); + + memcpy(java_address_bytes, &(address->s_addr), sizeof(address->s_addr)); + + env->ReleaseByteArrayElements(java_address, java_address_bytes, 0); + + return 0; +} + +/** + * Converts a native address structure to an InetAddress object. + * Throws a NullPointerException or an IOException in case of + * error. This is signaled by a return value of -1. The normal + * return value is 0. + */ +static int socketAddressToInetAddress(JNIEnv *env, + struct sockaddr_in *sockaddress, jobject inetaddress, int *port) { + + jbyteArray ipaddress; + int result; + + ipaddress = (jbyteArray)env->GetObjectField(inetaddress, + gCachedFields.iaddr_ipaddress); + + if (structInToJavaAddress(env, &sockaddress->sin_addr, ipaddress) < 0) { + return -1; + } + + *port = ntohs(sockaddress->sin_port); + + return 0; +} + +/** + * Converts an InetAddress object to a native address structure. + * Throws a NullPointerException or an IOException in case of + * error. This is signaled by a return value of -1. The normal + * return value is 0. + */ +static int inetAddressToSocketAddress(JNIEnv *env, + jobject inetaddress, int port, struct sockaddr_in *sockaddress) { + + jbyteArray ipaddress; + int result; + + ipaddress = (jbyteArray)env->GetObjectField(inetaddress, + gCachedFields.iaddr_ipaddress); + + memset(sockaddress, 0, sizeof(sockaddress)); + + sockaddress->sin_family = AF_INET; + sockaddress->sin_port = htons(port); + + if (javaAddressToStructIn(env, ipaddress, &(sockaddress->sin_addr)) < 0) { + return -1; + } + + return 0; +} + +static jobject structInToInetAddress(JNIEnv *env, struct in_addr *address) { + jbyteArray bytes; + int success; + + bytes = env->NewByteArray(4); + + if (bytes == NULL) { + return NULL; + } + + if (structInToJavaAddress(env, address, bytes) < 0) { + return NULL; + } + + return env->CallStaticObjectMethod(gCachedFields.iaddr_class, + gCachedFields.iaddr_getbyaddress, bytes); +} + +/** + * Answer a new java.lang.Boolean object. + * + * @param env pointer to the JNI library + * @param anInt the Boolean constructor argument + * + * @return the new Boolean + */ + +static jobject newJavaLangBoolean(JNIEnv * env, jint anInt) { + jclass tempClass; + jmethodID tempMethod; + + tempClass = gCachedFields.boolean_class; + tempMethod = gCachedFields.boolean_class_init; + return env->NewObject(tempClass, tempMethod, (jboolean) (anInt != 0)); +} + +/** + * Answer a new java.lang.Byte object. + * + * @param env pointer to the JNI library + * @param anInt the Byte constructor argument + * + * @return the new Byte + */ + +static jobject newJavaLangByte(JNIEnv * env, jbyte val) { + jclass tempClass; + jmethodID tempMethod; + + tempClass = gCachedFields.byte_class; + tempMethod = gCachedFields.byte_class_init; + return env->NewObject(tempClass, tempMethod, val); +} + +/** + * Answer a new java.lang.Integer object. + * + * @param env pointer to the JNI library + * @param anInt the Integer constructor argument + * + * @return the new Integer + */ + +static jobject newJavaLangInteger(JNIEnv * env, jint anInt) { + jclass tempClass; + jmethodID tempMethod; + + tempClass = gCachedFields.integer_class; + tempMethod = gCachedFields.integer_class_init; + return env->NewObject(tempClass, tempMethod, anInt); +} + +/** + * Answer a new java.lang.String object. + * + * @param env pointer to the JNI library + * @param anInt the byte[] constructor argument + * + * @return the new String + */ + +static jobject newJavaLangString(JNIEnv * env, jbyteArray bytes) { + jclass tempClass; + jmethodID tempMethod; + + tempClass = gCachedFields.string_class; + tempMethod = gCachedFields.string_class_init; + return env->NewObject(tempClass, tempMethod, (jbyteArray) bytes); +} + +/** + * Query OS for timestamp. + * Retrieve the current value of system clock and convert to milliseconds. + * + * @param[in] portLibrary The port library. + * + * @return 0 on failure, time value in milliseconds on success. + * @deprecated Use @ref time_hires_clock and @ref time_hires_delta + * + * technically, this should return I_64 since both timeval.tv_sec and + * timeval.tv_usec are long + */ + +static int time_msec_clock() { + struct timeval tp; + struct timezone tzp; + + gettimeofday(&tp, &tzp); + return (tp.tv_sec * 1000) + (tp.tv_usec / 1000); +} + +/** + * check if the passed sockaddr_in struct contains a localhost address + * + * @param[in] address pointer to the address to check + * + * @return 0 if the passed address isn't a localhost address + */ +static int isLocalhost(struct sockaddr_in *address) { + // return address == 127.0.0.1 + return (unsigned int) address->sin_addr.s_addr == 16777343; +} + +/** + * Answer the errorString corresponding to the errorNumber, if available. + * This function will answer a default error string, if the errorNumber is not + * recognized. + * + * This function will have to be reworked to handle internationalization + * properly, removing the explicit strings. + * + * @param anErrorNum the error code to resolve to a human readable string + * + * @return a human readable error string + */ + +static const char * netLookupErrorString(int anErrorNum) { + switch (anErrorNum) { + case SOCKERR_BADSOCKET: + return "Bad socket"; + case SOCKERR_NOTINITIALIZED: + return "Socket library uninitialized"; + case SOCKERR_BADAF: + return "Bad address family"; + case SOCKERR_BADPROTO: + return "Bad protocol"; + case SOCKERR_BADTYPE: + return "Bad type"; + case SOCKERR_SYSTEMBUSY: + return "System busy handling requests"; + case SOCKERR_SYSTEMFULL: + return "Too many sockets allocated"; + case SOCKERR_NOTCONNECTED: + return "Socket is not connected"; + case SOCKERR_INTERRUPTED: + return "The system call was cancelled"; + case SOCKERR_TIMEOUT: + return "The operation timed out"; + case SOCKERR_CONNRESET: + return "The connection was reset"; + case SOCKERR_WOULDBLOCK: + return "The nonblocking operation would block"; + case SOCKERR_ADDRNOTAVAIL: + return "The address is not available"; + case SOCKERR_ADDRINUSE: + return "The address is already in use"; + case SOCKERR_NOTBOUND: + return "The socket is not bound"; + case SOCKERR_UNKNOWNSOCKET: + return "Resolution of the FileDescriptor to socket failed"; + case SOCKERR_INVALIDTIMEOUT: + return "The specified timeout is invalid"; + case SOCKERR_FDSETFULL: + return "Unable to create an FDSET"; + case SOCKERR_TIMEVALFULL: + return "Unable to create a TIMEVAL"; + case SOCKERR_REMSOCKSHUTDOWN: + return "The remote socket has shutdown gracefully"; + case SOCKERR_NOTLISTENING: + return "Listen() was not invoked prior to accept()"; + case SOCKERR_NOTSTREAMSOCK: + return "The socket does not support connection-oriented service"; + case SOCKERR_ALREADYBOUND: + return "The socket is already bound to an address"; + case SOCKERR_NBWITHLINGER: + return "The socket is marked non-blocking & SO_LINGER is non-zero"; + case SOCKERR_ISCONNECTED: + return "The socket is already connected"; + case SOCKERR_NOBUFFERS: + return "No buffer space is available"; + case SOCKERR_HOSTNOTFOUND: + return "Authoritative Answer Host not found"; + case SOCKERR_NODATA: + return "Valid name, no data record of requested type"; + case SOCKERR_BOUNDORCONN: + return "The socket has not been bound or is already connected"; + case SOCKERR_OPNOTSUPP: + return "The socket does not support the operation"; + case SOCKERR_OPTUNSUPP: + return "The socket option is not supported"; + case SOCKERR_OPTARGSINVALID: + return "The socket option arguments are invalid"; + case SOCKERR_SOCKLEVELINVALID: + return "The socket level is invalid"; + case SOCKERR_TIMEOUTFAILURE: + return "The timeout operation failed"; + case SOCKERR_SOCKADDRALLOCFAIL: + return "Failed to allocate address structure"; + case SOCKERR_FDSET_SIZEBAD: + return "The calculated maximum size of the file descriptor set is bad"; + case SOCKERR_UNKNOWNFLAG: + return "The flag is unknown"; + case SOCKERR_MSGSIZE: + return "The datagram was too big to fit the specified buffer, so truncated"; + case SOCKERR_NORECOVERY: + return "The operation failed with no recovery possible"; + case SOCKERR_ARGSINVALID: + return "The arguments are invalid"; + case SOCKERR_BADDESC: + return "The socket argument is not a valid file descriptor"; + case SOCKERR_NOTSOCK: + return "The socket argument is not a socket"; + case SOCKERR_HOSTENTALLOCFAIL: + return "Unable to allocate the hostent structure"; + case SOCKERR_TIMEVALALLOCFAIL: + return "Unable to allocate the timeval structure"; + case SOCKERR_LINGERALLOCFAIL: + return "Unable to allocate the linger structure"; + case SOCKERR_IPMREQALLOCFAIL: + return "Unable to allocate the ipmreq structure"; + case SOCKERR_FDSETALLOCFAIL: + return "Unable to allocate the fdset structure"; + case SOCKERR_OPFAILED: + return "Operation failed"; + case SOCKERR_CONNECTION_REFUSED: + return "Connection refused"; + case SOCKERR_ENETUNREACH: + return "Network unreachable"; + case SOCKERR_EHOSTUNREACH: + return "No route to host"; + case SOCKERR_EPIPE: + return "Broken pipe"; + case SOCKERR_EACCES: + return "Permission denied (maybe missing INTERNET permission)"; + + default: +// LOGE("unknown socket error %d", anErrorNum); + return "unknown error"; + } +} + +static int convertError(int errorCode) { + switch (errorCode) { + case EBADF: + return SOCKERR_BADDESC; + case ENOBUFS: + return SOCKERR_NOBUFFERS; + case EOPNOTSUPP: + return SOCKERR_OPNOTSUPP; + case ENOPROTOOPT: + return SOCKERR_OPTUNSUPP; + case EINVAL: + return SOCKERR_SOCKLEVELINVALID; + case ENOTSOCK: + return SOCKERR_NOTSOCK; + case EINTR: + return SOCKERR_INTERRUPTED; + case ENOTCONN: + return SOCKERR_NOTCONNECTED; + case EAFNOSUPPORT: + return SOCKERR_BADAF; + /* note: CONNRESET not included because it has the same + * value as ECONNRESET and they both map to SOCKERR_CONNRESET */ + case ECONNRESET: + return SOCKERR_CONNRESET; + case EAGAIN: + return SOCKERR_WOULDBLOCK; + case EPROTONOSUPPORT: + return SOCKERR_BADPROTO; + case EFAULT: + return SOCKERR_ARGSINVALID; + case ETIMEDOUT: + return SOCKERR_TIMEOUT; + case ECONNREFUSED: + return SOCKERR_CONNECTION_REFUSED; + case ENETUNREACH: + return SOCKERR_ENETUNREACH; + case EACCES: + return SOCKERR_EACCES; + case EPIPE: + return SOCKERR_EPIPE; + case EHOSTUNREACH: + return SOCKERR_EHOSTUNREACH; + case EADDRINUSE: + return SOCKERR_ADDRINUSE; + case EADDRNOTAVAIL: + return SOCKERR_ADDRNOTAVAIL; + case EMSGSIZE: + return SOCKERR_MSGSIZE; + default: +// LOGE("unclassified errno %d (%s)", errorCode, strerror(errorCode)); + return SOCKERR_OPFAILED; + } +} + +static int sockSelect(int nfds, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *timeout) { + + int result = select(nfds, readfds, writefds, exceptfds, timeout); + + if (result < 0) { + if (errno == EINTR) { + result = SOCKERR_INTERRUPTED; + } else { + result = SOCKERR_OPFAILED; + } + } else if (result == 0) { + result = SOCKERR_TIMEOUT; + } + return result; +} + +#define SELECT_READ_TYPE 0 +#define SELECT_WRITE_TYPE 1 + +static int selectWait(int handle, int uSecTime, int type) { + fd_set fdset; + struct timeval time, *timePtr; + int result = 0; + int size = handle + 1; + + FD_ZERO(&fdset); + FD_SET(handle, &fdset); + + if (0 <= uSecTime) { + /* Use a timeout if uSecTime >= 0 */ + memset(&time, 0, sizeof(time)); + time.tv_usec = uSecTime; + timePtr = &time; + } else { + /* Infinite timeout if uSecTime < 0 */ + timePtr = NULL; + } + + if (type == SELECT_READ_TYPE) { + result = sockSelect(size, &fdset, NULL, NULL, timePtr); + } else { + result = sockSelect(size, NULL, &fdset, NULL, timePtr); + } + return result; +} + +static int pollSelectWait(JNIEnv *env, jobject fileDescriptor, int timeout, int type) { + /* now try reading the socket for the timespan timeout. + * if timeout is 0 try forever until the soclets gets ready or until an + * exception occurs. + */ + int pollTimeoutUSec = 100000, pollMsec = 100; + int finishTime = 0; + int timeLeft = timeout; + int hasTimeout = timeout > 0 ? 1 : 0; + int result = 0; + int handle; + + if (hasTimeout) { + finishTime = time_msec_clock() + timeout; + } + + int poll = 1; + + while (poll) { /* begin polling loop */ + + /* + * Fetch the handle every time in case the socket is closed. + */ + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_INTERRUPTED); + return -1; + } + + if (hasTimeout) { + + if (timeLeft - 10 < pollMsec) { + pollTimeoutUSec = timeLeft <= 0 ? 0 : (timeLeft * 1000); + } + + result = selectWait(handle, pollTimeoutUSec, type); + + /* + * because we are polling at a time smaller than timeout + * (presumably) lets treat an interrupt and timeout the same - go + * see if we're done timewise, and then just try again if not. + */ + if (SOCKERR_TIMEOUT == result || + SOCKERR_INTERRUPTED == result) { + + timeLeft = finishTime - time_msec_clock(); + + if (timeLeft <= 0) { + /* + * Always throw the "timeout" message because that is + * effectively what has happened, even if we happen to + * have been interrupted. + */ + jniThrowException(env, "java/net/SocketTimeoutException", + netLookupErrorString(SOCKERR_TIMEOUT)); + } else { + continue; // try again + } + + } else if (0 > result) { + log_socket_close(handle, result); + throwSocketException(env, result); + } + poll = 0; + + } else { /* polling with no timeout (why would you do this?)*/ + + result = selectWait(handle, pollTimeoutUSec, type); + + /* + * if interrupted (or a timeout) just retry + */ + if (SOCKERR_TIMEOUT == result || + SOCKERR_INTERRUPTED == result) { + + continue; // try again + } else if (0 > result) { + log_socket_close(handle, result); + throwSocketException(env, result); + } + poll = 0; + } + } /* end polling loop */ + + return result; +} + +/** + * A helper method, to set the connect context to a Long object. + * + * @param env pointer to the JNI library + * @param longclass Java Long Object + */ +void setConnectContext(JNIEnv *env,jobject longclass,jbyte * context) { + jclass descriptorCLS; + jfieldID descriptorFID; + descriptorCLS = env->FindClass("java/lang/Long"); + descriptorFID = env->GetFieldID(descriptorCLS, "value", "J"); + env->SetLongField(longclass, descriptorFID, (jlong)((jint)context)); +}; + +/** + * A helper method, to get the connect context. + * + * @param env pointer to the JNI library + * @param longclass Java Long Object + */ +jbyte *getConnectContext(JNIEnv *env, jobject longclass) { + jclass descriptorCLS; + jfieldID descriptorFID; + descriptorCLS = env->FindClass("java/lang/Long"); + descriptorFID = env->GetFieldID(descriptorCLS, "value", "J"); + return (jbyte*) ((jint)env->GetLongField(longclass, descriptorFID)); +}; + +// typical ip checksum +unsigned short ip_checksum(unsigned short* buffer, int size) { + register unsigned short * buf = buffer; + register int bufleft = size; + register unsigned long sum = 0; + + while (bufleft > 1) { + sum = sum + (*buf++); + bufleft = bufleft - sizeof(unsigned short ); + } + if (bufleft) { + sum = sum + (*(unsigned char*)buf); + } + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + + return (unsigned short )(~sum); +} + +/** + * Establish a connection to a peer with a timeout. This function is called + * repeatedly in order to carry out the connect and to allow other tasks to + * proceed on certain platforms. The caller must first call with + * step = SOCKET_STEP_START, if the result is SOCKERR_NOTCONNECTED it will then + * call it with step = CHECK until either another error or 0 is returned to + * indicate the connect is complete. Each time the function should sleep for no + * more than timeout milliseconds. If the connect succeeds or an error occurs, + * the caller must always end the process by calling the function with + * step = SOCKET_STEP_DONE + * + * @param[in] portLibrary The port library. + * @param[in] sock pointer to the unconnected local socket. + * @param[in] addr pointer to the sockaddr, specifying remote host/port. + * @param[in] timeout the timeout in milliseconds. If timeout is negative, + * perform a block operation. + * @param[in,out] pointer to context pointer. Filled in on first call and then + * to be passed into each subsequent call. + * + * @return 0, if no errors occurred, otherwise the (negative) error code. + */ +static int sockConnectWithTimeout(int handle, struct sockaddr_in addr, + unsigned int timeout, unsigned int step, jbyte *ctxt) { + int rc = 0; + struct timeval passedTimeout; + int errorVal; + socklen_t errorValLen = sizeof(int); + struct selectFDSet *context = NULL; + + if (SOCKET_STEP_START == step) { + + context = (struct selectFDSet *) ctxt; + + context->sock = handle; + context->nfds = handle + 1; + + if (useAdbNetworking && !isLocalhost(&addr)) { + + // LOGD("+connect to address 0x%08x (via adb)", + // addr.sin_addr.s_addr); + rc = adb_networking_connect_fd(handle, &addr); + // LOGD("-connect ret %d errno %d (via adb)", rc, errno); + + } else { + log_socket_connect(handle, ntohl(addr.sin_addr.s_addr), + ntohs(addr.sin_port)); + /* set the socket to non-blocking */ + int block = JNI_TRUE; + rc = ioctl(handle, FIONBIO, &block); + if (0 != rc) { + return convertError(rc); + } + + // LOGD("+connect to address 0x%08x (via normal) on handle %d", + // addr.sin_addr.s_addr, handle); + do { + rc = connect(handle, (struct sockaddr *) &addr, + sizeof(struct sockaddr)); + } while (rc < 0 && errno == EINTR); + // LOGD("-connect to address 0x%08x (via normal) returned %d", + // addr.sin_addr.s_addr, (int) rc); + + } + + if (rc == -1) { + rc = errno; + switch (rc) { + case EINTR: + return SOCKERR_ALREADYBOUND; + case EAGAIN: + case EINPROGRESS: + return SOCKERR_NOTCONNECTED; + default: + return convertError(rc); + } + } + + /* we connected right off the bat so just return */ + return rc; + + } else if (SOCKET_STEP_CHECK == step) { + /* now check if we have connected yet */ + + context = (struct selectFDSet *) ctxt; + + /* + * set the timeout value to be used. Because on some unix platforms we + * don't get notified when a socket is closed we only sleep for 100ms + * at a time + */ + passedTimeout.tv_sec = 0; + if (timeout > 100) { + passedTimeout.tv_usec = 100 * 1000; + } else if ((int)timeout >= 0) { + passedTimeout.tv_usec = timeout * 1000; + } + + /* initialize the FD sets for the select */ + FD_ZERO(&(context->exceptionSet)); + FD_ZERO(&(context->writeSet)); + FD_ZERO(&(context->readSet)); + FD_SET(context->sock, &(context->writeSet)); + FD_SET(context->sock, &(context->readSet)); + FD_SET(context->sock, &(context->exceptionSet)); + + rc = select(context->nfds, + &(context->readSet), + &(context->writeSet), + &(context->exceptionSet), + (int)timeout >= 0 ? &passedTimeout : NULL); + + /* if there is at least one descriptor ready to be checked */ + if (0 < rc) { + /* if the descriptor is in the write set we connected or failed */ + if (FD_ISSET(context->sock, &(context->writeSet))) { + + if (!FD_ISSET(context->sock, &(context->readSet))) { + /* ok we have connected ok */ + return 0; + } else { + /* ok we have more work to do to figure it out */ + if (getsockopt(context->sock, SOL_SOCKET, SO_ERROR, + &errorVal, &errorValLen) >= 0) { + return errorVal ? convertError(errorVal) : 0; + } else { + return convertError(errno); + } + } + } + + /* if the descriptor is in the exception set the connect failed */ + if (FD_ISSET(context->sock, &(context->exceptionSet))) { + if (getsockopt(context->sock, SOL_SOCKET, SO_ERROR, &errorVal, + &errorValLen) >= 0) { + return errorVal ? convertError(errorVal) : 0; + } + rc = errno; + return convertError(rc); + } + + } else if (rc < 0) { + /* something went wrong with the select call */ + rc = errno; + + /* if it was EINTR we can just try again. Return not connected */ + if (EINTR == rc) { + return SOCKERR_NOTCONNECTED; + } + + /* some other error occured so look it up and return */ + return convertError(rc); + } + + /* + * if we get here the timeout expired or the connect had not yet + * completed just indicate that the connect is not yet complete + */ + return SOCKERR_NOTCONNECTED; + } else if (SOCKET_STEP_DONE == step) { + /* we are done the connect or an error occured so clean up */ + if (handle != -1) { + int block = JNI_FALSE; + ioctl(handle, FIONBIO, &block); + } + return 0; + } + return SOCKERR_ARGSINVALID; +} + +/** + * Join/Leave the nominated multicast group on the specified socket. + * Implemented by setting the multicast 'add membership'/'drop membership' + * option at the HY_IPPROTO_IP level on the socket. + * + * Implementation note for multicast sockets in general: + * + * - This code is untested, because at the time of this writing multicast can't + * be properly tested on Android due to GSM routing restrictions. So it might + * or might not work. + * + * - The REUSEPORT socket option that Harmony employs is not supported on Linux + * and thus also not supported on Android. It's is not needed for multicast + * to work anyway (REUSEADDR should suffice). + * + * @param env pointer to the JNI library. + * @param socketP pointer to the hysocket to join/leave on. + * @param optVal pointer to the InetAddress, the multicast group to join/drop. + * + * @exception SocketException if an error occurs during the call + */ +static void mcastAddDropMembership (JNIEnv * env, int handle, jobject optVal, + int ignoreIF, int setSockOptVal) { + int result; + struct ip_mreq ipmreqP; + struct sockaddr_in sockaddrP; + int length = sizeof(struct ip_mreq); + socklen_t lengthIF = sizeof(struct sockaddr_in); + + /* + * JNI objects needed to access the information in the optVal oject + * passed in. The object passed in is a GenericIPMreq object + */ + jclass cls; + jfieldID multiaddrID; + jfieldID interfaceAddrID; + jobject multiaddr; + jobject interfaceAddr; + + /* + * check whether we are getting an InetAddress or an Generic IPMreq, for now + * we support both so that we will not break the tests + */ + if (env->IsInstanceOf (optVal, gCachedFields.iaddr_class)) { + + ipmreqP.imr_interface.s_addr = htonl(INADDR_ANY); + if (!ignoreIF) { + + result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF, &sockaddrP, + &lengthIF); + + if (0 != result) { + throwSocketException (env, convertError(errno)); + return; + } + + memcpy(&(ipmreqP.imr_interface.s_addr), &(sockaddrP.sin_addr), 4); + } + + result = inetAddressToSocketAddress(env, optVal, 0, &sockaddrP); + + if (result < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + memcpy(&(ipmreqP.imr_multiaddr.s_addr), &(sockaddrP.sin_addr), 4); + + result = setsockopt(handle, IPPROTO_IP, setSockOptVal, &ipmreqP, length); + if (0 != result) { + throwSocketException (env, convertError(errno)); + return; + } + + } else { + + /* we need the multicast address regardless of the type of address */ + cls = env->GetObjectClass(optVal); + multiaddrID = env->GetFieldID(cls, "multiaddr", "Ljava/net/InetAddress;"); + multiaddr = env->GetObjectField(optVal, multiaddrID); + + result = inetAddressToSocketAddress(env, multiaddr, 0, &sockaddrP); + + if (result < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + memcpy(&(ipmreqP.imr_multiaddr.s_addr), &(sockaddrP.sin_addr), 4); + + /* we need to use an IP_MREQ as it is an IPV4 address */ + interfaceAddrID = env->GetFieldID(cls, "interfaceAddr", + "Ljava/net/InetAddress;"); + interfaceAddr = env->GetObjectField(optVal, interfaceAddrID); + + ipmreqP.imr_interface.s_addr = htonl(INADDR_ANY); + + /* + * if an interfaceAddr was passed then use that value, otherwise set the + * interface to all 0 to indicate the system should select the interface + * used + */ + if (!ignoreIF) { + if (NULL != interfaceAddr) { + + result = inetAddressToSocketAddress(env, interfaceAddr, 0, + &sockaddrP); + + if (result < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + memcpy(&(ipmreqP.imr_interface.s_addr), &(sockaddrP.sin_addr), 4); + + } + } + + /* join/drop the multicast address */ + result = setsockopt(handle, IPPROTO_IP, setSockOptVal, &ipmreqP, length); + if (0 != result) { + throwSocketException (env, convertError(errno)); + return; + } + } +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_oneTimeInitializationImpl(JNIEnv* env, jobject obj, + jboolean jcl_supports_ipv6) { + // LOGD("ENTER oneTimeInitializationImpl of OSNetworkSystem"); + + char useAdbNetworkingProperty[PROPERTY_VALUE_MAX]; + char adbConnectedProperty[PROPERTY_VALUE_MAX]; + + property_get("android.net.use-adb-networking", useAdbNetworkingProperty, ""); + property_get("adb.connected", adbConnectedProperty, ""); + + if (strlen((char *)useAdbNetworkingProperty) > 0 + && strlen((char *)adbConnectedProperty) > 0) { + useAdbNetworking = 1; + } + + memset(&gCachedFields, 0, sizeof(gCachedFields)); + + // initializing InetAddress + + jclass iaddrclass = env->FindClass("java/net/InetAddress"); + + if (iaddrclass == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "java.net.InetAddress"); + return; + } + + gCachedFields.iaddr_class = (jclass) env->NewGlobalRef(iaddrclass); + + jmethodID iaddrclassinit = env->GetMethodID(iaddrclass, "", "()V"); + + if (iaddrclassinit == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", "InetAddress.()"); + return; + } + + gCachedFields.iaddr_class_init = iaddrclassinit; + + jmethodID iaddrgetbyaddress = env->GetStaticMethodID(iaddrclass, + "getByAddress", "([B)Ljava/net/InetAddress;"); + + if (iaddrgetbyaddress == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", + "InetAddress.getByAddress(byte[] val)"); + return; + } + + gCachedFields.iaddr_getbyaddress = iaddrgetbyaddress; + + jmethodID iaddrgetaddress = env->GetStaticMethodID(iaddrclass, + "getByAddress", "([B)Ljava/net/InetAddress;"); + + + + + jfieldID iaddripaddress = env->GetFieldID(iaddrclass, "ipaddress", "[B"); + + if (iaddripaddress == NULL) { + jniThrowException(env, "java/lang/NoSuchFieldError", + "Can't find field InetAddress.ipaddress"); + return; + } + + gCachedFields.iaddr_ipaddress = iaddripaddress; + + // get the GenericIPMreq class + + jclass genericipmreqclass = env->FindClass("org/apache/harmony/luni/net/GenericIPMreq"); + + if (genericipmreqclass == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "org.apache.harmony.luni.net.GenericIPMreq"); + return; + } + + gCachedFields.genericipmreq_class = (jclass) env->NewGlobalRef(genericipmreqclass); + + // initializing Integer + + jclass integerclass = env->FindClass("java/lang/Integer"); + + if (integerclass == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "java.lang.Integer"); + return; + } + + jmethodID integerclassinit = env->GetMethodID(integerclass, "", "(I)V"); + + if (integerclassinit == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", + "Integer.(int val)"); + return; + } + + jfieldID integerclassvalue = env->GetFieldID(integerclass, "value", "I"); + + if (integerclassvalue == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", "Integer.value"); + return; + } + + gCachedFields.integer_class = (jclass) env->NewGlobalRef(integerclass); + gCachedFields.integer_class_init = integerclassinit; + gCachedFields.integer_class_value = integerclassvalue; + + // initializing Boolean + + jclass booleanclass = env->FindClass("java/lang/Boolean"); + + if (booleanclass == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "java.lang.Boolean"); + return; + } + + jmethodID booleanclassinit = env->GetMethodID(booleanclass, "", "(Z)V"); + + if (booleanclassinit == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", + "Boolean.(boolean val)"); + return; + } + + jfieldID booleanclassvalue = env->GetFieldID(booleanclass, "value", "Z"); + + if (booleanclassvalue == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", "Boolean.value"); + return; + } + + gCachedFields.boolean_class = (jclass) env->NewGlobalRef(booleanclass); + gCachedFields.boolean_class_init = booleanclassinit; + gCachedFields.boolean_class_value = booleanclassvalue; + + // initializing Byte + + jclass byteclass = env->FindClass("java/lang/Byte"); + + if (byteclass == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "java.lang.Byte"); + return; + } + + jmethodID byteclassinit = env->GetMethodID(byteclass, "", "(B)V"); + + if (byteclassinit == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", + "Byte.(byte val)"); + return; + } + + jfieldID byteclassvalue = env->GetFieldID(byteclass, "value", "B"); + + if (byteclassvalue == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", "Byte.value"); + return; + } + + gCachedFields.byte_class = (jclass) env->NewGlobalRef(byteclass); + gCachedFields.byte_class_init = byteclassinit; + gCachedFields.byte_class_value = byteclassvalue; + + // initializing String + + jclass stringclass = env->FindClass("java/lang/String"); + + if (stringclass == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "java.lang.String"); + return; + } + + jmethodID stringclassinit = env->GetMethodID(stringclass, "", "([B)V"); + + if (stringclassinit == NULL) { + jniThrowException(env, "java/lang/NoSuchMethodError", + "String.(byte[] val)"); + return; + } + + gCachedFields.string_class = (jclass) env->NewGlobalRef(stringclass); + gCachedFields.string_class_init = stringclassinit; + + // initializing ScoketImpl + + jclass socketimplclass = env->FindClass("java/net/SocketImpl"); + + if (socketimplclass == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "java.net.SocketImpl"); + return; + } + + jfieldID socketimplport = env->GetFieldID(socketimplclass, "port", "I"); + + if (socketimplport == NULL) { + jniThrowException(env, "java/lang/NoSuchFieldError", "SocketImpl.port"); + return; + } + + jfieldID socketimpladdress = env->GetFieldID(socketimplclass, "address", + "Ljava/net/InetAddress;"); + + if (socketimpladdress == NULL) { + jniThrowException(env, "java/lang/NoSuchFieldError", + "SocketImpl.address"); + return; + } + + gCachedFields.socketimpl_address = socketimpladdress; + gCachedFields.socketimpl_port = socketimplport; + + gCachedFields.dpack_class = env->FindClass("java/net/DatagramPacket"); + if (gCachedFields.dpack_class == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "java.net.DatagramPacket"); + return; + } + + gCachedFields.dpack_address = env->GetFieldID(gCachedFields.dpack_class, + "address", "Ljava/net/InetAddress;"); + if (gCachedFields.dpack_address == NULL) { + jniThrowException(env, "java/lang/NoSuchFieldError", + "DatagramPacket.address"); + return; + } + + gCachedFields.dpack_port = env->GetFieldID(gCachedFields.dpack_class, + "port", "I"); + if (gCachedFields.dpack_port == NULL) { + jniThrowException(env, "java/lang/NoSuchFieldError", + "DatagramPacket.port"); + return; + } + + gCachedFields.dpack_length = env->GetFieldID(gCachedFields.dpack_class, + "length", "I"); + if (gCachedFields.dpack_length == NULL) { + jniThrowException(env, "java/lang/NoSuchFieldError", + "DatagramPacket.length"); + return; + } + + gCachedFields.fd_class = env->FindClass("java/io/FileDescriptor"); + if (gCachedFields.fd_class == NULL) { + jniThrowException(env, "java/lang/ClassNotFoundException", + "java.io.FileDescriptor"); + return; + } + gCachedFields.descriptor = env->GetFieldID(gCachedFields.fd_class, "descriptor", "I"); + if (gCachedFields.descriptor == NULL) { + jniThrowException(env, "java/lang/NoSuchFieldError", + "FileDescriptor.descriptor"); + return; + } + +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_createSocketImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jboolean preferIPv4Stack) { + // LOGD("ENTER createSocketImpl"); + + int ret = socket(PF_INET, SOCK_STREAM, 0); + + if (ret < 0) { + int err = convertError(errno); + throwSocketException(env, err); + return; + } + + jniSetFileDescriptorOfFD(env, fileDescriptor, ret); + + return; +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_createDatagramSocketImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jboolean preferIPv4Stack) { + // LOGD("ENTER createDatagramSocketImpl"); + + int ret = socket(PF_INET, SOCK_DGRAM, 0); + + if (ret < 0) { + int err = convertError(errno); + throwSocketException(env, err); + return; + } + + jniSetFileDescriptorOfFD(env, fileDescriptor, ret); + + return; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_readSocketDirectImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint address, jint offset, jint count, + jint timeout) { + // LOGD("ENTER readSocketDirectImpl"); + + int handle; + jbyte *message = (jbyte *)address; + int result, ret, localCount; + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + result = selectWait(handle, timeout, SELECT_READ_TYPE); + + if (0 > result) { + return 0; + } + + localCount = (count < 65536) ? count : 65536; + + do { + ret = recv(handle, (jbyte *) message, localCount, SOCKET_NOFLAGS); + } while (ret < 0 && errno == EINTR); + + if (0 == ret) { + return -1; + } else if (ret == -1) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + return 0; + } + add_recv_stats(handle, ret); + return ret; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_readSocketImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jbyteArray data, jint offset, jint count, + jint timeout) { + // LOGD("ENTER readSocketImpl"); + + jbyte *message; + int result, localCount; + + jbyte internalBuffer[BUFFERSIZE]; + + localCount = (count < 65536) ? count : 65536; + + if (localCount > BUFFERSIZE) { + message = (jbyte*)malloc(localCount * sizeof(jbyte)); + if (message == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", + "couldn't allocate enough memory for readSocket"); + return 0; + } + } else { + message = (jbyte *)internalBuffer; + } + + result = Java_org_sipdroid_net_impl_OSNetworkSystem_readSocketDirectImpl(env, clazz, fileDescriptor, + (jint) message, offset, count, timeout); + + if (result > 0) { + env->SetByteArrayRegion(data, offset, result, (jbyte *)message); + } + + if (((jbyte *)message) != internalBuffer) { + free(( jbyte *)message); + } + + return result; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_writeSocketDirectImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint address, jint offset, jint count) { + // LOGD("ENTER writeSocketDirectImpl"); + + int handle; + jbyte *message = (jbyte *)address; + int result = 0, sent = 0; + + if (count <= 0) { + return 0; + } + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + result = send(handle, (jbyte *) message, (int) count, SOCKET_NOFLAGS); + if (result < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + + if (SOCKERR_WOULDBLOCK == err){ + jclass socketExClass,errorCodeExClass; + jmethodID errorCodeExConstructor, socketExConstructor,socketExCauseMethod; + jobject errorCodeEx, socketEx; + const char* errorMessage = netLookupErrorString(err); + jstring errorMessageString = env->NewStringUTF(errorMessage); + + errorCodeExClass = env->FindClass("org/apache/harmony/luni/util/ErrorCodeException"); + if (!errorCodeExClass){ + return 0; + } + errorCodeExConstructor = env->GetMethodID(errorCodeExClass,"","(I)V"); + if (!errorCodeExConstructor){ + return 0; + } + errorCodeEx = env->NewObject(errorCodeExClass,errorCodeExConstructor,err); + + socketExClass = env->FindClass("java/net/SocketException"); + if (!socketExClass) { + return 0; + } + socketExConstructor = env->GetMethodID(socketExClass,"","(Ljava/lang/String;)V"); + if (!socketExConstructor) { + return 0; + } + socketEx = env->NewObject(socketExClass, socketExConstructor, errorMessageString); + socketExCauseMethod = env->GetMethodID(socketExClass,"initCause","(Ljava/lang/Throwable;)Ljava/lang/Throwable;"); + env->CallObjectMethod(socketEx,socketExCauseMethod,errorCodeEx); + env->Throw((jthrowable)socketEx); + return 0; + } + throwSocketException(env, err); + return 0; + } + + add_send_stats(handle, result); + return result; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_writeSocketImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jbyteArray data, jint offset, jint count) { + // LOGD("ENTER writeSocketImpl"); + + jbyte *message; + int sent = 0; + jint result = 0; + +/* TODO: ARRAY PINNING */ +#define INTERNAL_SEND_BUFFER_MAX 512 + jbyte internalBuffer[INTERNAL_SEND_BUFFER_MAX]; + + if (count > INTERNAL_SEND_BUFFER_MAX) { + message = (jbyte*)malloc(count * sizeof( jbyte)); + if (message == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", + "couldn't allocate enough memory for writeSocket"); + return 0; + } + } else { + message = (jbyte *)internalBuffer; + } + + env->GetByteArrayRegion(data, offset, count, message); + + result = Java_org_sipdroid_net_impl_OSNetworkSystem_writeSocketDirectImpl(env, clazz, fileDescriptor, + (jint) message, offset, count); + + if (( jbyte *)message != internalBuffer) { + free(( jbyte *)message); + } +#undef INTERNAL_SEND_BUFFER_MAX + return result; +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_setNonBlockingImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jboolean nonblocking) { + // LOGD("ENTER setNonBlockingImpl"); + + int handle; + int result; + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + int block = nonblocking; + + result = ioctl(handle, FIONBIO, &block); + + if (result == -1) { + throwSocketException(env, convertError(errno)); + } +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_connectSocketImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint trafficClass, jobject inetAddr, jint port); + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_connectWithTimeoutSocketImpl(JNIEnv* env, + jclass clazz, jobject fileDescriptor, jint timeout, jint trafficClass, + jobject inetAddr, jint port, jint step, jbyteArray passContext) { + // LOGD("ENTER connectWithTimeoutSocketImpl"); + + int handle; + int result = 0; + struct sockaddr_in address; + jbyte *context = NULL; + + memset(&address, 0, sizeof(address)); + + address.sin_family = AF_INET; + + result = inetAddressToSocketAddress(env, inetAddr, port, + (struct sockaddr_in *) &address); + + if (result < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return result; + } + + // Check if we're using adb networking and redirect in case it is used. + if (useAdbNetworking && !isLocalhost(&address)) { + return Java_org_sipdroid_net_impl_OSNetworkSystem_connectSocketImpl(env, clazz, fileDescriptor, + trafficClass, inetAddr, port); + } + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return -1; + } + + address.sin_port = htons(port); + + context = (jbyte *)env->GetPrimitiveArrayCritical(passContext, NULL); + + switch (step) { + case SOCKET_CONNECT_STEP_START: + result = sockConnectWithTimeout(handle, address, 0, + SOCKET_STEP_START, context); + break; + case SOCKET_CONNECT_STEP_CHECK: + result = sockConnectWithTimeout(handle, address, timeout, + SOCKET_STEP_CHECK, context); + break; + } + + env->ReleasePrimitiveArrayCritical(passContext, context, JNI_ABORT); + + if (0 == result) { + /* connected , so stop here */ + sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_DONE, NULL); + } else if (result != SOCKERR_NOTCONNECTED) { + /* can not connect... */ + sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_DONE, NULL); + if (result == SOCKERR_EACCES) { + jniThrowException(env, "java/lang/SecurityException", + netLookupErrorString(result)); + } else { + jniThrowException(env, "java/net/ConnectException", + netLookupErrorString(result)); + } + } + + return result; +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_connectStreamWithTimeoutSocketImpl(JNIEnv* env, + jclass clazz, jobject fileDescriptor, jint remotePort, jint timeout, + jint trafficClass, jobject inetAddr) { + // LOGD("ENTER connectStreamWithTimeoutSocketImpl"); + + int result = 0; + int handle; + struct sockaddr_in address; + jbyte *context = NULL; + int remainingTimeout = timeout; + int passedTimeout = 0; + int finishTime = 0; + int blocking = 0; + char hasTimeout = timeout > 0; + + /* if a timeout was specified calculate the finish time value */ + if (hasTimeout) { + finishTime = time_msec_clock() + (int) timeout; + } + + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } else { + result = inetAddressToSocketAddress(env, inetAddr, remotePort, + (struct sockaddr_in *) &address); + + if (result < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + // Check if we're using adb networking and redirect in case it is used. + if (useAdbNetworking && !isLocalhost(&address)) { + int retVal = Java_org_sipdroid_net_impl_OSNetworkSystem_connectSocketImpl(env, clazz, + fileDescriptor, trafficClass, inetAddr, remotePort); + if (retVal != 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + } + return; + } + + /* + * we will be looping checking for when we are connected so allocate + * the descriptor sets that we will use + */ + context =(jbyte *) malloc(sizeof(struct selectFDSet)); + + if (NULL == context) { + throwSocketException(env, SOCKERR_NOBUFFERS); + return; + } + + result = sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_START, context); + if (0 == result) { + /* ok we connected right away so we are done */ + sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_DONE, context); + goto bail; + } else if (result != SOCKERR_NOTCONNECTED) { + log_socket_close(handle, result); + sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_DONE, + context); + /* we got an error other than NOTCONNECTED so we cannot continue */ + if (SOCKERR_EACCES == result) { + jniThrowException(env, "java/lang/SecurityException", + netLookupErrorString(result)); + } else { + throwSocketException(env, result); + } + goto bail; + } + + while (SOCKERR_NOTCONNECTED == result) { + passedTimeout = remainingTimeout; + + /* + * ok now try and connect. Depending on the platform this may sleep + * for up to passedTimeout milliseconds + */ + result = sockConnectWithTimeout(handle, address, passedTimeout, + SOCKET_STEP_CHECK, context); + + /* + * now check if the socket is still connected. + * Do it here as some platforms seem to think they + * are connected if the socket is closed on them. + */ + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + sockConnectWithTimeout(handle, address, 0, + SOCKET_STEP_DONE, context); + throwSocketException(env, SOCKERR_BADSOCKET); + goto bail; + } + + /* + * check if we are now connected, + * if so we can finish the process and return + */ + if (0 == result) { + sockConnectWithTimeout(handle, address, 0, + SOCKET_STEP_DONE, context); + goto bail; + } + + /* + * if the error is SOCKERR_NOTCONNECTED then we have not yet + * connected and we may not be done yet + */ + if (SOCKERR_NOTCONNECTED == result) { + /* check if the timeout has expired */ + if (hasTimeout) { + remainingTimeout = finishTime - time_msec_clock(); + if (remainingTimeout <= 0) { + log_socket_close(handle, result); + sockConnectWithTimeout(handle, address, 0, + SOCKET_STEP_DONE, context); + jniThrowException(env, + "java/net/SocketTimeoutException", + netLookupErrorString(result)); + goto bail; + } + } else { + remainingTimeout = 100; + } + } else { + log_socket_close(handle, result); + sockConnectWithTimeout(handle, address, remainingTimeout, + SOCKET_STEP_DONE, context); + if ((SOCKERR_CONNRESET == result) || + (SOCKERR_CONNECTION_REFUSED == result) || + (SOCKERR_ADDRNOTAVAIL == result) || + (SOCKERR_ADDRINUSE == result) || + (SOCKERR_ENETUNREACH == result)) { + jniThrowException(env, "java/net/ConnectException", + netLookupErrorString(result)); + } else if (SOCKERR_EACCES == result) { + jniThrowException(env, "java/lang/SecurityException", + netLookupErrorString(result)); + } else { + throwSocketException(env, result); + } + goto bail; + } + } + } + +bail: + + /* free the memory for the FD set */ + if (context != NULL) { + free(context); + } +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_connectSocketImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint trafficClass, jobject inetAddr, jint port) { + //LOGD("ENTER direct-call connectSocketImpl\n"); + + struct sockaddr_in address; + int ret; + int handle; + jbyteArray java_in_addr; + + memset(&address, 0, sizeof(address)); + + address.sin_family = AF_INET; + + ret = inetAddressToSocketAddress(env, inetAddr, port, + (struct sockaddr_in *) &address); + + if (ret < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return ret; + } + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return -1; + } + + address.sin_port = htons(port); + + if (useAdbNetworking && !isLocalhost(&address)) { + + // LOGD("+connect to address 0x%08x port %d (via adb)", + // address.sin_addr.s_addr, (int) port); + ret = adb_networking_connect_fd(handle, &address); + // LOGD("-connect ret %d errno %d (via adb)", ret, errno); + + } else { + + // call this method with a timeout of zero + Java_org_sipdroid_net_impl_OSNetworkSystem_connectStreamWithTimeoutSocketImpl(env, clazz, + fileDescriptor, port, 0, trafficClass, inetAddr); + if (env->ExceptionOccurred() != 0) { + return -1; + } else { + return 0; + } + + } + + if (ret < 0) { + jniThrowException(env, "java/net/ConnectException", + netLookupErrorString(convertError(errno))); + return ret; + } + + return ret; +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_socketBindImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint port, jobject inetAddress) { + // LOGD("ENTER socketBindImpl"); + + struct sockaddr_in sockaddress; + int ret; + int handle; + + ret = inetAddressToSocketAddress(env, inetAddress, port, + (struct sockaddr_in *) &sockaddress); + + if (ret < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + ret = bind(handle, (const sockaddr*)&sockaddress, sizeof(sockaddress)); + + if (ret < 0) { + jniThrowException(env, "java/net/BindException", + netLookupErrorString(convertError(errno))); + return; + } +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_listenStreamSocketImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint backlog) { + // LOGD("ENTER listenStreamSocketImpl"); + + int ret; + int handle; + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + ret = listen(handle, backlog); + + if (ret < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + return; + } +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_availableStreamImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor) { + // LOGD("ENTER availableStreamImpl"); + + int handle; + char message[BUFFERSIZE]; + + int result; + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + do { + result = selectWait(handle, 1, SELECT_READ_TYPE); + + if (SOCKERR_TIMEOUT == result) { + // The read operation timed out, so answer 0 bytes available + return 0; + } else if (SOCKERR_INTERRUPTED == result) { + continue; + } else if (0 > result) { + log_socket_close(handle, result); + throwSocketException(env, result); + return 0; + } + } while (SOCKERR_INTERRUPTED == result); + + result = recv(handle, (jbyte *) message, BUFFERSIZE, MSG_PEEK); + + if (0 > result) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + return 0; + } + add_recv_stats(handle, result); + return result; +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_acceptSocketImpl(JNIEnv* env, jclass clazz, + jobject fdServer, jobject newSocket, jobject fdnewSocket, jint timeout) { + // LOGD("ENTER acceptSocketImpl"); + + union { + struct sockaddr address; + struct sockaddr_in in_address; + } sa; + + int ret; + int retFD; + int result; + int handle; + socklen_t addrlen; + + if (newSocket == NULL) { + throwNullPointerException(env); + return; + } + + result = pollSelectWait(env, fdServer, timeout, SELECT_READ_TYPE); + + if (0 > result) { + return; + } + + handle = jniGetFDFromFileDescriptor(env, fdServer); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + do { + addrlen = sizeof(sa); + ret = accept(handle, &(sa.address), &addrlen); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + return; + } + + retFD = ret; + + /* For AF_INET / inetOrLocal == true only: put + * peer address and port in instance variables + * We don't bother for UNIX domain sockets, since most peers are + * anonymous anyway + */ + if (sa.address.sa_family == AF_INET) { + // inetOrLocal should also be true + + jobject inetAddress; + + inetAddress = structInToInetAddress(env, &(sa.in_address.sin_addr)); + + if (inetAddress == NULL) { + close(retFD); + newSocket = NULL; + return; + } + + env->SetObjectField(newSocket, + gCachedFields.socketimpl_address, inetAddress); + + env->SetIntField(newSocket, gCachedFields.socketimpl_port, + ntohs(sa.in_address.sin_port)); + } + + jniSetFileDescriptorOfFD(env, fdnewSocket, retFD); +} + +extern "C" jboolean Java_org_sipdroid_net_impl_OSNetworkSystem_supportsUrgentDataImpl(JNIEnv* env, + jclass clazz, jobject fileDescriptor) { + // LOGD("ENTER supportsUrgentDataImpl"); + + int handle; + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (handle == 0 || handle == -1) { + return JNI_FALSE; + } + + return JNI_TRUE; +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_sendUrgentDataImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jbyte value) { + // LOGD("ENTER sendUrgentDataImpl"); + + int handle; + int result; + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + result = send(handle, (jbyte *) &value, 1, MSG_OOB); + if (result < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + } +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_connectDatagramImpl2(JNIEnv* env, jclass clazz, + jobject fd, jint port, jint trafficClass, jobject inetAddress) { + // LOGD("ENTER connectDatagramImpl2"); + + int handle = jniGetFDFromFileDescriptor(env, fd); + + struct sockaddr_in sockAddr; + int ret; + + ret = inetAddressToSocketAddress(env, inetAddress, port, &sockAddr); + + if (ret < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + log_socket_connect(handle, ntohl(sockAddr.sin_addr.s_addr), port); + int result = connect(handle, (struct sockaddr *)&sockAddr, sizeof(sockAddr)); + if (result < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + } +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_disconnectDatagramImpl(JNIEnv* env, jclass clazz, + jobject fd) { + // LOGD("ENTER disconnectDatagramImpl"); + + int handle = jniGetFDFromFileDescriptor(env, fd); + + struct sockaddr_in *sockAddr; + socklen_t sockAddrLen = sizeof(struct sockaddr_in); + sockAddr = (struct sockaddr_in *) malloc(sockAddrLen); + memset(sockAddr, 0, sockAddrLen); + + sockAddr->sin_family = AF_UNSPEC; + int result = connect(handle, (struct sockaddr *)sockAddr, sockAddrLen); + free(sockAddr); + + if (result < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + } +} + +extern "C" jboolean Java_org_sipdroid_net_impl_OSNetworkSystem_socketBindImpl2(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint port, jboolean bindToDevice, + jobject inetAddress) { + // LOGD("ENTER socketBindImpl2"); + + struct sockaddr_in sockaddress; + int ret; + int handle; + + ret = inetAddressToSocketAddress(env, inetAddress, port, + (struct sockaddr_in *) &sockaddress); + + if (ret < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + ret = bind(handle, (const sockaddr*)&sockaddress, sizeof(sockaddress)); + + if (ret < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + jniThrowException(env, "java/net/BindException", netLookupErrorString(err)); + return 0; + } + + return 0; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_peekDatagramImpl(JNIEnv* env, jclass clazz, + jobject fd, jobject sender, jint receiveTimeout) { + // LOGD("ENTER peekDatagramImpl"); + + int port = -1; + + int result = pollSelectWait (env, fd, receiveTimeout, SELECT_READ_TYPE); + if (0> result) { + return (jint) 0; + } + + int handle = jniGetFDFromFileDescriptor(env, fd); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + struct sockaddr_in sockAddr; + socklen_t sockAddrLen = sizeof(sockAddr); + + int length = recvfrom(handle, NULL, 0, MSG_PEEK, + (struct sockaddr *)&sockAddr, &sockAddrLen); + + if (length < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + return 0; + } + + if (socketAddressToInetAddress(env, &sockAddr, sender, &port) < 0) { + throwIOExceptionStr(env, "Address conversion failed"); + return -1; + } + add_recv_stats(handle, length); + return port; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_receiveDatagramDirectImpl(JNIEnv* env, jclass clazz, + jobject fd, jobject packet, jint address, jint offset, jint length, + jint receiveTimeout, jboolean peek) { + // LOGD("ENTER receiveDatagramDirectImpl"); + + int result = pollSelectWait (env, fd, receiveTimeout, SELECT_READ_TYPE); + if (0 > result) { + return (jint) 0; + } + + int handle = jniGetFDFromFileDescriptor(env, fd); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + struct sockaddr_in sockAddr; + socklen_t sockAddrLen = sizeof(sockAddr); + + int mode = peek ? MSG_PEEK : 0; + + int actualLength = recvfrom(handle, (char*)(address + offset), length, mode, + (struct sockaddr *)&sockAddr, &sockAddrLen); + + if (actualLength < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + return 0; + } + + if (packet != NULL) { + int port = ntohs(sockAddr.sin_port); + jobject sender = env->GetObjectField(packet, gCachedFields.dpack_address); + //__android_log_print(ANDROID_LOG_INFO,"OSNetwork","Sender address %x ", sender); + if (sender != 0) { + jbyteArray addr = (jbyteArray)env->GetObjectField(sender, gCachedFields.iaddr_ipaddress); + //__android_log_print(ANDROID_LOG_INFO,"OSNetwork","Sender Recup"); + if ((structInToJavaAddress(env, &sockAddr.sin_addr, addr)) < 0) { + jniThrowException(env, "java/net/SocketException", + "Could not set address of packet."); + return 0; + } + } + else { + jbyteArray addr = env->NewByteArray(sizeof(struct in_addr)); + if ((structInToJavaAddress(env, &sockAddr.sin_addr, addr)) < 0) { + jniThrowException(env, "java/net/SocketException", + "Could not set address of packet."); + return 0; + } + sender = env->CallStaticObjectMethod( + gCachedFields.iaddr_class, gCachedFields.iaddr_getbyaddress, + addr); + + } + env->SetObjectField(packet, gCachedFields.dpack_address, sender); + env->SetIntField(packet, gCachedFields.dpack_port, port); + env->SetIntField(packet, gCachedFields.dpack_length, actualLength); + } + add_recv_stats(handle, actualLength); + return actualLength; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_receiveDatagramImpl(JNIEnv* env, jclass clazz, + jobject fd, jobject packet, jbyteArray data, jint offset, jint length, + jint receiveTimeout, jboolean peek) { + // LOGD("ENTER receiveDatagramImpl"); + + int localLength = (length < 65536) ? length : 65536; + jbyte *bytes = (jbyte*) malloc(localLength); + if (bytes == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", + "couldn't allocate enough memory for receiveDatagram"); + return 0; + } + + int actualLength = Java_org_sipdroid_net_impl_OSNetworkSystem_receiveDatagramDirectImpl(env, clazz, fd, + packet, (jint)bytes, offset, localLength, receiveTimeout, peek); + + if (actualLength > 0) { + env->SetByteArrayRegion(data, offset, actualLength, bytes); + } + free(bytes); + + return actualLength; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_recvConnectedDatagramDirectImpl(JNIEnv* env, + jclass clazz, jobject fd, jobject packet, jint address, jint offset, + jint length, jint receiveTimeout, jboolean peek) { + // LOGD("ENTER receiveConnectedDatagramDirectImpl"); + + int result = pollSelectWait (env, fd, receiveTimeout, SELECT_READ_TYPE); + + if (0 > result) { + return 0; + } + + int handle = jniGetFDFromFileDescriptor(env, fd); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + int mode = peek ? MSG_PEEK : 0; + + int actualLength = recvfrom(handle, + (char*)(address + offset), length, mode, NULL, NULL); + + if (actualLength < 0) { + jniThrowException(env, "java/net/PortUnreachableException", ""); + return 0; + } + + if ( packet != NULL) { + env->SetIntField(packet, gCachedFields.dpack_length, actualLength); + } + add_recv_stats(handle, actualLength); + return actualLength; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_recvConnectedDatagramImpl(JNIEnv* env, jclass clazz, + jobject fd, jobject packet, jbyteArray data, jint offset, jint length, + jint receiveTimeout, jboolean peek) { + // LOGD("ENTER receiveConnectedDatagramImpl"); + + int localLength = (length < 65536) ? length : 65536; + jbyte *bytes = (jbyte*) malloc(localLength); + if (bytes == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", + "couldn't allocate enough memory for recvConnectedDatagram"); + return 0; + } + + int actualLength = Java_org_sipdroid_net_impl_OSNetworkSystem_recvConnectedDatagramDirectImpl(env, + clazz, fd, packet, (jint)bytes, offset, localLength, + receiveTimeout, peek); + + if (actualLength > 0) { + env->SetByteArrayRegion(data, offset, actualLength, bytes); + } + free(bytes); + + return actualLength; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_sendDatagramDirectImpl(JNIEnv* env, jclass clazz, + jobject fd, jint address, jint offset, jint length, jint port, + jboolean bindToDevice, jint trafficClass, jobject inetAddress) { + // LOGD("ENTER sendDatagramDirectImpl"); + + int result = 0; + + int handle = jniGetFDFromFileDescriptor(env, fd); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + struct sockaddr_in receiver; + + if (inetAddressToSocketAddress(env, inetAddress, port, &receiver) < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + result = sendto(handle, (char*)(address + offset), length, SOCKET_NOFLAGS, + (struct sockaddr*)&receiver, sizeof(receiver)); + + if (result < 0) { + int err = convertError(errno); + if ((SOCKERR_CONNRESET == err) + || (SOCKERR_CONNECTION_REFUSED == err)) { + return 0; + } else { + log_socket_close(handle, err); + throwSocketException(env, err); + return 0; + } + } + add_send_stats(handle, result); + return result; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_sendDatagramImpl(JNIEnv* env, jclass clazz, + jobject fd, jbyteArray data, jint offset, jint length, jint port, + jboolean bindToDevice, jint trafficClass, jobject inetAddress) { + // LOGD("ENTER sendDatagramImpl"); + + jbyte *bytes = env->GetByteArrayElements(data, NULL); + int actualLength = Java_org_sipdroid_net_impl_OSNetworkSystem_sendDatagramDirectImpl(env, clazz, fd, + (jint)bytes, offset, length, port, bindToDevice, trafficClass, + inetAddress); + env->ReleaseByteArrayElements(data, bytes, JNI_ABORT); + + return actualLength; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_sendConnectedDatagramDirectImpl(JNIEnv* env, + jclass clazz, jobject fd, jint address, jint offset, jint length, + jboolean bindToDevice) { + // LOGD("ENTER sendConnectedDatagramDirectImpl"); + + int handle = jniGetFDFromFileDescriptor(env, fd); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + int result = send(handle, (char*)(address + offset), length, 0); + + if (result < 0) { + int err = convertError(errno); + if ((SOCKERR_CONNRESET == err) || (SOCKERR_CONNECTION_REFUSED == err)) { + return 0; + } else { + log_socket_close(handle, err); + throwSocketException(env, err); + return 0; + } + } + add_send_stats(handle, length); + return result; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_sendConnectedDatagramImpl(JNIEnv* env, jclass clazz, + jobject fd, jbyteArray data, jint offset, jint length, + jboolean bindToDevice) { + // LOGD("ENTER sendConnectedDatagramImpl"); + + jbyte *bytes = env->GetByteArrayElements(data, NULL); + int actualLength = Java_org_sipdroid_net_impl_OSNetworkSystem_sendConnectedDatagramDirectImpl(env, + clazz, fd, (jint)bytes, offset, length, bindToDevice); + env->ReleaseByteArrayElements(data, bytes, JNI_ABORT); + + return actualLength; +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_createServerStreamSocketImpl(JNIEnv* env, + jclass clazz, jobject fileDescriptor, jboolean preferIPv4Stack) { + // LOGD("ENTER createServerStreamSocketImpl"); + + if (fileDescriptor == NULL) { + throwNullPointerException(env); + return; + } + + int handle = socket(PF_INET, SOCK_STREAM, 0); + + if (handle < 0) { + int err = convertError(errno); + throwSocketException(env, err); + return; + } + + jniSetFileDescriptorOfFD(env, fileDescriptor, handle); + + int value = 1; + + setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(int)); +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_createMulticastSocketImpl(JNIEnv* env, + jclass clazz, jobject fileDescriptor, jboolean preferIPv4Stack) { + // LOGD("ENTER createMulticastSocketImpl"); + + int handle = socket(PF_INET, SOCK_DGRAM, 0); + + if (handle < 0) { + int err = convertError(errno); + throwSocketException(env, err); + return; + } + + jniSetFileDescriptorOfFD(env, fileDescriptor, handle); + + int value = 1; + + // setsockopt(handle, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(jbyte)); + setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(int)); +} + +/* + * @param timeout in milliseconds. If zero, block until data received + */ +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_receiveStreamImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jbyteArray data, jint offset, jint count, + jint timeout) { + // LOGD("ENTER receiveStreamImpl"); + + int result; + int handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + // Cap read length to available buf size + int spaceAvailable = env->GetArrayLength(data) - offset; + int localCount = count < spaceAvailable? count : spaceAvailable; + + jboolean isCopy; + jbyte *body = env->GetByteArrayElements(data, &isCopy); + + // set timeout + struct timeval tv; + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + setsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, + sizeof(struct timeval)); + + do { + result = recv(handle, body + offset, localCount, SOCKET_NOFLAGS); + } while (result < 0 && errno == EINTR); + + env->ReleaseByteArrayElements(data, body, 0); + + /* + * If no bytes are read, return -1 to signal 'endOfFile' + * to the Java input stream + */ + if (0 < result) { + add_recv_stats(handle, result); + return result; + } else if (0 == result) { + return -1; + } else { + // If EAGAIN or EWOULDBLOCK, read timed out + if (errno == EAGAIN || errno == EWOULDBLOCK) { + jniThrowException(env, "java/net/SocketTimeoutException", + netLookupErrorString(SOCKERR_TIMEOUT)); + } else { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + } + return 0; + } +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_sendStreamImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jbyteArray data, jint offset, jint count) { + // LOGD("ENTER sendStreamImpl"); + + int handle = 0; + int result = 0, sent = 0; + + jboolean isCopy; + jbyte *message = env->GetByteArrayElements(data, &isCopy); + + // Cap write length to available buf size + int spaceAvailable = env->GetArrayLength(data) - offset; + if (count > spaceAvailable) count = spaceAvailable; + + while (sent < count) { + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (handle == 0 || handle == -1) { + throwSocketException(env, + sent == 0 ? SOCKERR_BADSOCKET : SOCKERR_INTERRUPTED); + env->ReleaseByteArrayElements(data, message, 0); + return 0; + } + + // LOGD("before select %d", count); + selectWait(handle, SEND_RETRY_TIME, SELECT_WRITE_TYPE); + result = send(handle, (jbyte *)message + offset + sent, + (int) count - sent, SOCKET_NOFLAGS); + + if (result < 0) { + result = errno; + if (result == EAGAIN ||result == EWOULDBLOCK) { + // LOGD("write blocked %d", sent); + continue; + } + env->ReleaseByteArrayElements(data, message, 0); + int err = convertError(result); + log_socket_close(handle, err); + throwSocketException(env, err); + return 0; + } + sent += result; + } + + env->ReleaseByteArrayElements(data, message, 0); + add_send_stats(handle, sent); + return sent; +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_shutdownInputImpl(JNIEnv* env, jobject obj, + jobject fileDescriptor) { + // LOGD("ENTER shutdownInputImpl"); + + int ret; + int handle; + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + ret = shutdown(handle, SHUT_RD); + + if (ret < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + return; + } +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_shutdownOutputImpl(JNIEnv* env, jobject obj, + jobject fileDescriptor) { + // LOGD("ENTER shutdownOutputImpl"); + + int ret; + int handle; + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + return; + } + + ret = shutdown(handle, SHUT_WR); + + if (ret < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + return; + } +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_sendDatagramImpl2(JNIEnv* env, jclass clazz, + jobject fd, jbyteArray data, jint offset, jint length, jint port, + jobject inetAddress) { + // LOGD("ENTER sendDatagramImpl2"); + + jbyte *message; + jbyte nhostAddrBytes[4]; + unsigned short nPort; + int result = 0, sent = 0; + int handle = 0; + struct sockaddr_in sockaddrP; + + if (inetAddress != NULL) { + + result = inetAddressToSocketAddress(env, inetAddress, port, + (struct sockaddr_in *) &sockaddrP); + + if (result < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + + handle = jniGetFDFromFileDescriptor(env, fd); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return 0; + } + } + + message = (jbyte*) malloc(length * sizeof(jbyte)); + if (message == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", + "couldn't allocate enough memory for readSocket"); + return 0; + } + + env->GetByteArrayRegion(data, offset, length, message); + + while (sent < length) { + handle = jniGetFDFromFileDescriptor(env, fd); + + if (handle == 0 || handle == -1) { + throwSocketException(env, + sent == 0 ? SOCKERR_BADSOCKET : SOCKERR_INTERRUPTED); + free(message); + return 0; + } + + result = sendto(handle, (char *) (message + sent), + (int) (length - sent), SOCKET_NOFLAGS, + (struct sockaddr *) &sockaddrP, sizeof(sockaddrP)); + + if (result < 0) { + int err = convertError(errno); + log_socket_close(handle, err); + throwSocketException(env, err); + free(message); + return 0; + } + + sent += result; + } + + free(message); + add_send_stats(handle, sent); + return sent; +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_selectImpl(JNIEnv* env, jclass clazz, + jobjectArray readFDArray, jobjectArray writeFDArray, jint countReadC, + jint countWriteC, jintArray outFlags, jlong timeout) { + // LOGD("ENTER selectImpl"); + + struct timeval timeP; + int result = 0; + int size = 0; + jobject gotFD; + fd_set *fdset_read,*fdset_write; + int handle; + jboolean isCopy ; + jint *flagArray; + int val; + unsigned int time_sec = (unsigned int)timeout/1000; + unsigned int time_msec = (unsigned int)(timeout%1000); + + fdset_read = (fd_set *)malloc(sizeof(fd_set)); + fdset_write = (fd_set *)malloc(sizeof(fd_set)); + + FD_ZERO(fdset_read); + FD_ZERO(fdset_write); + + for (val = 0; valGetObjectArrayElement(readFDArray,val); + + handle = jniGetFDFromFileDescriptor(env, gotFD); + + FD_SET(handle, fdset_read); + + if (0 > (size - handle)) { + size = handle; + } + } + + for (val = 0; valGetObjectArrayElement(writeFDArray,val); + + handle = jniGetFDFromFileDescriptor(env, gotFD); + + FD_SET(handle, fdset_write); + + if (0 > (size - handle)) { + size = handle; + } + } + + /* the size is the max_fd + 1 */ + size =size + 1; + + if (0 > size) { + result = SOCKERR_FDSET_SIZEBAD; + } else { + /* only set when timeout >= 0 (non-block)*/ + if (0 <= timeout) { + + timeP.tv_sec = time_sec; + timeP.tv_usec = time_msec*1000; + + result = sockSelect(size, fdset_read, fdset_write, NULL, &timeP); + + } else { + result = sockSelect(size, fdset_read, fdset_write, NULL, NULL); + } + } + + if (0 < result) { + /*output the result to a int array*/ + flagArray = env->GetIntArrayElements(outFlags, &isCopy); + + for (val=0; valGetObjectArrayElement(readFDArray,val); + + handle = jniGetFDFromFileDescriptor(env, gotFD); + + if (FD_ISSET(handle,fdset_read)) { + flagArray[val] = SOCKET_OP_READ; + } else { + flagArray[val] = SOCKET_OP_NONE; + } + } + + for (val=0; valGetObjectArrayElement(writeFDArray,val); + + handle = jniGetFDFromFileDescriptor(env, gotFD); + + if (FD_ISSET(handle,fdset_write)) { + flagArray[val+countReadC] = SOCKET_OP_WRITE; + } else { + flagArray[val+countReadC] = SOCKET_OP_NONE; + } + } + + env->ReleaseIntArrayElements(outFlags, flagArray, 0); + } + + free(fdset_write); + free(fdset_read); + + /* return both correct and error result, let java handle the exception*/ + return result; +} + +extern "C" jobject Java_org_sipdroid_net_impl_OSNetworkSystem_getSocketLocalAddressImpl(JNIEnv* env, + jclass clazz, jobject fileDescriptor, jboolean preferIPv6Addresses) { + // LOGD("ENTER getSocketLocalAddressImpl"); + + struct sockaddr_in addr; + socklen_t addrLen = sizeof(addr); + + memset(&addr, 0, addrLen); + + int handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + int result; + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_UNKNOWNSOCKET); + return NULL; + } + + result = getsockname(handle, (struct sockaddr *)&addr, &addrLen); + + // Spec says ignore all errors + + return structInToInetAddress(env, &(addr.sin_addr)); + +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_getSocketLocalPortImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jboolean preferIPv6Addresses) { + // LOGD("ENTER getSocketLocalPortImpl"); + + struct sockaddr_in addr; + socklen_t addrLen = sizeof(addr); + + int handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + int result; + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_UNKNOWNSOCKET); + return 0; + } + + result = getsockname(handle, (struct sockaddr *)&addr, &addrLen); + + if (0 != result) { + // The java spec does not indicate any exceptions on this call + return 0; + } else { + return ntohs(addr.sin_port); + } +} + +extern "C" jobject Java_org_sipdroid_net_impl_OSNetworkSystem_getSocketOptionImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint anOption) { + // LOGD("ENTER getSocketOptionImpl"); + + int handle; + int intValue = 0; + socklen_t intSize = sizeof(int); + unsigned char byteValue = 0; + socklen_t byteSize = sizeof(unsigned char); + int result; + struct sockaddr_in sockVal; + socklen_t sockSize = sizeof(sockVal); + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return NULL; + } + + switch ((int) anOption & 0xffff) { + case JAVASOCKOPT_SO_LINGER: { + struct linger lingr; + socklen_t size = sizeof(struct linger); + result = getsockopt(handle, SOL_SOCKET, SO_LINGER, &lingr, &size); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + if (!lingr.l_onoff) { + intValue = -1; + } else { + intValue = lingr.l_linger; + } + return newJavaLangInteger(env, intValue); + } + case JAVASOCKOPT_TCP_NODELAY: { + if ((anOption >> 16) & BROKEN_TCP_NODELAY) { + return NULL; + } + result = getsockopt(handle, IPPROTO_TCP, TCP_NODELAY, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangBoolean(env, intValue); + } + case JAVASOCKOPT_MCAST_TTL: { + if ((anOption >> 16) & BROKEN_MULTICAST_TTL) { + return newJavaLangByte(env, 0); + } + result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_TTL, &byteValue, &byteSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangByte(env, (jbyte)(byteValue & 0xFF)); + } + case JAVASOCKOPT_MCAST_INTERFACE: { + if ((anOption >> 16) & BROKEN_MULTICAST_IF) { + return NULL; + } + result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF, &sockVal, &sockSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return structInToInetAddress(env, &(sockVal.sin_addr)); + } + case JAVASOCKOPT_SO_SNDBUF: { + result = getsockopt(handle, SOL_SOCKET, SO_SNDBUF, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangInteger(env, intValue); + } + case JAVASOCKOPT_SO_RCVBUF: { + result = getsockopt(handle, SOL_SOCKET, SO_RCVBUF, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangInteger(env, intValue); + } + case JAVASOCKOPT_SO_BROADCAST: { + result = getsockopt(handle, SOL_SOCKET, SO_BROADCAST, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangBoolean(env, intValue); + } + case JAVASOCKOPT_SO_REUSEADDR: { + result = getsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangBoolean(env, intValue); + } + case JAVASOCKOPT_SO_KEEPALIVE: { + result = getsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangBoolean(env, intValue); + } + case JAVASOCKOPT_SO_OOBINLINE: { + result = getsockopt(handle, SOL_SOCKET, SO_OOBINLINE, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangBoolean(env, intValue); + } + case JAVASOCKOPT_IP_MULTICAST_LOOP: { + result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_LOOP, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangBoolean(env, intValue); + } + case JAVASOCKOPT_IP_TOS: { + result = getsockopt(handle, IPPROTO_IP, IP_TOS, &intValue, &intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangInteger(env, intValue); + } + case JAVASOCKOPT_SO_RCVTIMEOUT: { + struct timeval timeout; + socklen_t size = sizeof(timeout); + result = getsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, &timeout, &size); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + return newJavaLangInteger(env, timeout.tv_sec * 1000 + timeout.tv_usec/1000); + } + default: { + throwSocketException(env, SOCKERR_OPTUNSUPP); + return NULL; + } + } + +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_setSocketOptionImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor, jint anOption, jobject optVal) { + // LOGD("ENTER setSocketOptionImpl"); + + int handle, result; + int intVal, intSize = sizeof(int); + unsigned char byteVal, byteSize = sizeof(unsigned char); + struct sockaddr_in sockVal; + int sockSize = sizeof(sockVal); + + if (env->IsInstanceOf(optVal, gCachedFields.integer_class)) { + intVal = (int) env->GetIntField(optVal, gCachedFields.integer_class_value); + } else if (env->IsInstanceOf(optVal, gCachedFields.boolean_class)) { + intVal = (int) env->GetBooleanField(optVal, gCachedFields.boolean_class_value); + } else if (env->IsInstanceOf(optVal, gCachedFields.byte_class)) { + byteVal = (int) env->GetByteField(optVal, gCachedFields.byte_class_value); + } else if (env->IsInstanceOf(optVal, gCachedFields.iaddr_class)) { + if (inetAddressToSocketAddress(env, optVal, 0, &sockVal) < 0) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + } else if (env->IsInstanceOf(optVal, gCachedFields.genericipmreq_class)) { + // we'll use optVal directly + } else { + throwSocketException(env, SOCKERR_OPTUNSUPP); + return; + } + + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + switch ((int) anOption & 0xffff) { + case JAVASOCKOPT_SO_LINGER: { + struct linger lingr; + lingr.l_onoff = intVal > 0 ? 1 : 0; + lingr.l_linger = intVal; + result = setsockopt(handle, SOL_SOCKET, SO_LINGER, &lingr, + sizeof(struct linger)); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_TCP_NODELAY: { + if ((anOption >> 16) & BROKEN_TCP_NODELAY) { + return; + } + result = setsockopt(handle, IPPROTO_TCP, TCP_NODELAY, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_MCAST_TTL: { + if ((anOption >> 16) & BROKEN_MULTICAST_TTL) { + return; + } + result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_TTL, &byteVal, byteSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_MCAST_ADD_MEMBERSHIP: { + mcastAddDropMembership(env, handle, optVal, + (anOption >> 16) & BROKEN_MULTICAST_IF, IP_ADD_MEMBERSHIP); + return; + } + + case JAVASOCKOPT_MCAST_DROP_MEMBERSHIP: { + mcastAddDropMembership(env, handle, optVal, + (anOption >> 16) & BROKEN_MULTICAST_IF, IP_DROP_MEMBERSHIP); + return; + } + + case JAVASOCKOPT_MCAST_INTERFACE: { + if ((anOption >> 16) & BROKEN_MULTICAST_IF) { + return; + } + result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF, &sockVal, sockSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_SO_SNDBUF: { + result = setsockopt(handle, SOL_SOCKET, SO_SNDBUF, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_SO_RCVBUF: { + result = setsockopt(handle, SOL_SOCKET, SO_RCVBUF, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_SO_BROADCAST: { + result = setsockopt(handle, SOL_SOCKET, SO_BROADCAST, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_SO_REUSEADDR: { + result = setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + case JAVASOCKOPT_SO_KEEPALIVE: { + result = setsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_SO_OOBINLINE: { + result = setsockopt(handle, SOL_SOCKET, SO_OOBINLINE, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_IP_MULTICAST_LOOP: { + result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_LOOP, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_IP_TOS: { + result = setsockopt(handle, IPPROTO_IP, IP_TOS, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_REUSEADDR_AND_REUSEPORT: { + // SO_REUSEPORT doesn't need to get set on this System + result = setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &intVal, intSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + case JAVASOCKOPT_SO_RCVTIMEOUT: { + struct timeval timeout; + timeout.tv_sec = intVal / 1000; + timeout.tv_usec = (intVal % 1000) * 1000; + result = setsockopt(handle, SOL_SOCKET, SO_RCVTIMEO, &timeout, + sizeof(struct timeval)); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + + default: { + throwSocketException(env, SOCKERR_OPTUNSUPP); + } + } +} + +extern "C" jint Java_org_sipdroid_net_impl_OSNetworkSystem_getSocketFlagsImpl(JNIEnv* env, jclass clazz) { + // LOGD("ENTER getSocketFlagsImpl"); + + // Not implemented by harmony + return 0; +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_socketCloseImpl(JNIEnv* env, jclass clazz, + jobject fileDescriptor) { + // LOGD("ENTER socketCloseImpl"); + + int handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + + if (handle == 0 || handle == -1) { + throwSocketException(env, SOCKERR_BADSOCKET); + return; + } + + log_socket_close(handle, SOCKET_CLOSE_LOCAL); + + jniSetFileDescriptorOfFD(env, fileDescriptor, -1); + + close(handle); +} + +extern "C" jobject Java_org_sipdroid_net_impl_OSNetworkSystem_getHostByAddrImpl(JNIEnv* env, jclass clazz, + jbyteArray addrStr) { + // LOGD("ENTER getHostByAddrImpl"); + + if (addrStr == NULL) { + throwNullPointerException(env); + return JNI_FALSE; + } + + jstring address = (jstring)newJavaLangString(env, addrStr); + jstring result; + const char* addr = env->GetStringUTFChars(address, NULL); + + struct hostent* ent = gethostbyaddr(addr, strlen(addr), AF_INET); + + if (ent != NULL && ent->h_name != NULL) { + result = env->NewStringUTF(ent->h_name); + } else { + result = NULL; + } + + env->ReleaseStringUTFChars(address, addr); + + return result; +} + +extern "C" jobject Java_org_sipdroid_net_impl_OSNetworkSystem_getHostByNameImpl(JNIEnv* env, jclass clazz, + jstring nameStr, jboolean preferIPv6Addresses) { + // LOGD("ENTER getHostByNameImpl"); + + if (nameStr == NULL) { + throwNullPointerException(env); + return NULL; + } + + const char* name = env->GetStringUTFChars(nameStr, NULL); + + if (useAdbNetworking) { + + union { + struct in_addr a; + jbyte j[4]; + } outaddr; + + // LOGD("ADB networking: +gethostbyname '%s'", name); + int err; + err = adb_networking_gethostbyname(name, &(outaddr.a)); + + env->ReleaseStringUTFChars(nameStr, name); +#if 0 + LOGD("ADB networking: -gethostbyname err %d addr 0x%08x %u.%u.%u.%u", + err, (unsigned int)outaddr.a.s_addr, + outaddr.j[0],outaddr.j[1], + outaddr.j[2],outaddr.j[3]); +#endif + + if (err < 0) { + return NULL; + } else { + jbyteArray addr = env->NewByteArray(4); + env->SetByteArrayRegion(addr, 0, 4, outaddr.j); + return addr; + } + } else { + + // normal case...no adb networking + struct hostent* ent = gethostbyname(name); + + env->ReleaseStringUTFChars(nameStr, name); + + if (ent != NULL && ent->h_length > 0) { + jbyteArray addr = env->NewByteArray(4); + jbyte v[4]; + memcpy(v, ent->h_addr, 4); + env->SetByteArrayRegion(addr, 0, 4, v); + return addr; + } else { + return NULL; + } + } +} + +extern "C" void Java_org_sipdroid_net_impl_OSNetworkSystem_setInetAddressImpl(JNIEnv* env, jobject obj, + jobject sender, jbyteArray address) { + // LOGD("ENTER setInetAddressImpl"); + + env->SetObjectField(sender, gCachedFields.iaddr_ipaddress, address); +} + +/* +extern "C" jobject Java_org_sipdroid_net_impl_OSNetworkSystem_inheritedChannelImpl(JNIEnv* env, jobject obj) { + // LOGD("ENTER inheritedChannelImpl"); + + int socket = 0; + int opt; + socklen_t length = sizeof(opt); + int socket_type; + struct sockaddr_in local_addr; + struct sockaddr_in remote_addr; + jclass channel_class, socketaddr_class, serverSocket_class, socketImpl_class; + jobject channel_object = NULL, socketaddr_object, serverSocket_object; + jobject fd_object, addr_object, localAddr_object, socketImpl_object; + jfieldID port_field, socketaddr_field, bound_field, fd_field; + jfieldID serverSocket_field, socketImpl_field, addr_field, localAddr_field; + jmethodID channel_new; + jbyteArray addr_array; + struct sockaddr_in *sock; + jbyte * address; + jbyte * localAddr; + jboolean jtrue = JNI_TRUE; + + if (0 != getsockopt(socket, SOL_SOCKET, SO_TYPE, &opt, &length)) { + return NULL; + } + if (SOCK_STREAM !=opt && SOCK_DGRAM !=opt) { + return NULL; + } + socket_type = opt; + + length = sizeof(struct sockaddr); + if (0 != getsockname(socket, (struct sockaddr *)&local_addr, &length)) { + return NULL; + } else { + if (AF_INET != local_addr.sin_family || length != sizeof(struct sockaddr)) { + return NULL; + } + localAddr = (jbyte*) malloc(sizeof(jbyte)*4); + if (NULL == localAddr) { + return NULL; + } + memcpy (localAddr, &(local_addr.sin_addr.s_addr), 4); + } + if (0 != getpeername(socket, (struct sockaddr *)&remote_addr, &length)) { + remote_addr.sin_port = 0; + remote_addr.sin_addr.s_addr = 0; + address = (jbyte*) malloc(sizeof(jbyte)*4); + bzero(address, sizeof(jbyte)*4); + } else { + if (AF_INET != remote_addr.sin_family + || length != sizeof(struct sockaddr)) { + return NULL; + } + address = (jbyte*) malloc(sizeof(jbyte)*4); + memcpy (address, &(remote_addr.sin_addr.s_addr), 4); + } + + // analysis end, begin pack to java + if (SOCK_STREAM == opt) { + if (remote_addr.sin_port!=0) { + //socket + channel_class = env->FindClass( + "org/apache/harmony/nio/internal/SocketChannelImpl"); + if (NULL == channel_class) { + goto clean; + } + + channel_new = env->GetMethodID(channel_class, "", "()V"); + if (NULL == channel_new) { + goto clean; + } + channel_object = env->NewObject(channel_class, channel_new); + if (NULL == channel_object) { + goto clean; + } + // new and set FileDescript + + fd_field = env->GetFieldID(channel_class, "fd", + "java/io/FielDescriptor"); + fd_object = env->GetObjectField(channel_object, fd_field); + if (NULL == fd_object) { + goto clean; + } + + jniSetFileDescriptorOfFD(env, fd_object, socket); + + // local port + port_field = env->GetFieldID(channel_class, "localPort", "I"); + env->SetIntField(channel_object, port_field, + ntohs(local_addr.sin_port)); + + // new and set remote addr + addr_object = env->NewObject(gCachedFields.iaddr_class, + gCachedFields.iaddr_class_init); + if (NULL == addr_object) { + goto clean; + } + socketaddr_class = env->FindClass("java/net/InetSocketAddress"); + socketaddr_field = env->GetFieldID(channel_class, "connectAddress", + "Ljava/net/InetSocketAddress;"); + socketaddr_object = env->GetObjectField(channel_object, + socketaddr_field); + if (NULL == socketaddr_object) { + goto clean; + } + addr_field = env->GetFieldID(socketaddr_class, "addr", + "Ljava/net/InetAddress;"); + env->SetObjectField(socketaddr_object, addr_field, addr_object); + addr_array = env->NewByteArray((jsize)4); + env->SetByteArrayRegion(addr_array, (jsize)0, (jsize)4, address); + env->SetObjectField(addr_object, gCachedFields.iaddr_ipaddress, + addr_array); + + // localAddr + socketaddr_class = env->FindClass("java/net/InetSocketAddress"); + socketaddr_field = env->GetFieldID(channel_class, "connectAddress", + "Ljava/net/InetSocketAddress;"); + socketaddr_object = env->GetObjectField(channel_object, + socketaddr_field); + + localAddr_field = env->GetFieldID(channel_class, "localAddress", + "Ljava/net/InetAddress;"); + localAddr_object = env->NewObject(gCachedFields.iaddr_class, + gCachedFields.iaddr_class_init); + jfieldID socketaddr_field = env->GetFieldID(channel_class, + "connectAddress", "Ljava/net/InetSocketAddress;"); + jobject socketaddr_object = env->GetObjectField(channel_object, + socketaddr_field); + env->SetObjectField(socketaddr_object, localAddr_field, + localAddr_object); + if (NULL == localAddr_object) { + goto clean; + } + addr_array = env->NewByteArray((jsize)4); + env->SetByteArrayRegion(addr_array, (jsize)0, (jsize)4, localAddr); + env->SetObjectField(localAddr_object, gCachedFields.iaddr_ipaddress, + addr_array); + + + // set port + port_field = env->GetFieldID(socketaddr_class, "port", "I"); + env->SetIntField(socketaddr_object, port_field, + ntohs(remote_addr.sin_port)); + + // set bound + if (0 != local_addr.sin_port) { + bound_field = env->GetFieldID(channel_class, "isBound", "Z"); + env->SetBooleanField(channel_object, bound_field, jtrue); + } + + } else { + //serverSocket + channel_class = env->FindClass( + "org/apache/harmony/nio/internal/ServerSocketChannelImpl"); + if (NULL == channel_class) { + goto clean; + } + + channel_new = env->GetMethodID(channel_class, "", "()V"); + if (NULL == channel_new) { + goto clean; + } + channel_object = env->NewObject(channel_class, channel_new); + if (NULL == channel_object) { + goto clean; + } + + serverSocket_field = env->GetFieldID(channel_class, "socket", + "Ljava/net/ServerSocket;"); + serverSocket_class = env->FindClass("Ljava/net/ServerSocket;"); + serverSocket_object = env->GetObjectField(channel_object, + serverSocket_field); + // set bound + if (0 != local_addr.sin_port) { + bound_field = env->GetFieldID(channel_class, "isBound", "Z"); + env->SetBooleanField(channel_object, bound_field, jtrue); + bound_field = env->GetFieldID(serverSocket_class, "isBound", "Z"); + env->SetBooleanField(serverSocket_object, bound_field, jtrue); + } + // localAddr + socketImpl_class = env->FindClass("java/net/SocketImpl"); + socketImpl_field = env->GetFieldID(channel_class, "impl", + "Ljava/net/SocketImpl;"); + socketImpl_object = env->GetObjectField(channel_object, + socketImpl_field); + if (NULL == socketImpl_object) { + goto clean; + } + + localAddr_field = env->GetFieldID(channel_class, "localAddress", + "Ljava/net/InetAddress;"); + localAddr_object = env->NewObject(gCachedFields.iaddr_class, + gCachedFields.iaddr_class_init); + if (NULL == localAddr_object) { + goto clean; + } + env->SetObjectField(socketImpl_object, localAddr_field, + localAddr_object); + addr_array = env->NewByteArray((jsize)4); + env->SetByteArrayRegion(addr_array, (jsize)0, (jsize)4, localAddr); + env->SetObjectField(localAddr_object, + gCachedFields.iaddr_ipaddress, addr_array); + + // set port + port_field = env->GetFieldID(socketImpl_class, "localport", "I"); + env->SetIntField(socketImpl_object, port_field, + ntohs(local_addr.sin_port)); + } + } else { + //Datagram Socket + // new DatagramChannel + channel_class = env->FindClass( + "org/apache/harmony/nio/internal/DatagramChannelImpl"); + if (NULL == channel_class) { + goto clean; + } + + channel_new = env->GetMethodID(channel_class, "", "()V"); + if (NULL == channel_new) { + goto clean; + } + channel_object = env->NewObject(channel_class, channel_new); + if (NULL == channel_object) { + goto clean; + } + + // new and set FileDescript + fd_field = env->GetFieldID(channel_class, "fd", "java/io/FileDescriptor"); + fd_object = env->GetObjectField(channel_object, fd_field); + if (NULL == fd_object) { + goto clean; + } + + jniSetFileDescriptorOfFD(env, fd_object, socket); + + port_field = env->GetFieldID(channel_class, "localPort", "I"); + env->SetIntField(channel_object, port_field, ntohs(local_addr.sin_port)); + + // new and set remote addr + addr_object = env->NewObject(gCachedFields.iaddr_class, + gCachedFields.iaddr_class_init); + if (NULL == addr_object) { + goto clean; + } + socketaddr_class = env->FindClass("java/net/InetSocketAddress"); + socketaddr_field = env->GetFieldID(channel_class, "connectAddress", + "Ljava/net/InetSocketAddress;"); + socketaddr_object = env->GetObjectField(channel_object, socketaddr_field); + if (NULL == socketaddr_object) { + goto clean; + } + addr_field = env->GetFieldID(socketaddr_class, "addr", + "Ljava/net/InetAddress;"); + env->SetObjectField(socketaddr_object, addr_field, addr_object); + addr_array = env->NewByteArray((jsize)4); + env->SetByteArrayRegion(addr_array, (jsize)0, (jsize)4, address); + env->SetObjectField(addr_object, gCachedFields.iaddr_ipaddress, addr_array); + + // set bound + if (0 != local_addr.sin_port) { + bound_field = env->GetFieldID(channel_class, "isBound", "Z"); + env->SetBooleanField(channel_object, bound_field, jtrue); + } + } +clean: + free(address); + free(localAddr); + return channel_object; +} +*/