static void ConvertIn6AddrToByteArray(uint8_t* buffer, int32_t bufferLength, const in6_addr& addr) { #if HAVE_IN6_U assert(bufferLength == ARRAY_SIZE(addr.__in6_u.__u6_addr8)); memcpy(buffer, addr.__in6_u.__u6_addr8, UnsignedCast(bufferLength)); #else assert(bufferLength == ARRAY_SIZE(addr.__u6_addr.__u6_addr8)); memcpy(buffer, addr.__u6_addr.__u6_addr8, UnsignedCast(bufferLength)); #endif }
extern "C" int32_t IPAddressToString(const uint8_t* address, int32_t addressLength, bool isIPv6, uint8_t* string, int32_t stringLength, uint32_t scope /* = 0*/) { assert(address != nullptr); assert((addressLength == NUM_BYTES_IN_IPV6_ADDRESS) || (addressLength == NUM_BYTES_IN_IPV4_ADDRESS)); assert(string != nullptr); // These constants differ per platform so the managed side uses the bigger value; therefore, check that // the length is between the two lengths assert((stringLength >= INET_ADDRSTRLEN) && (stringLength <= INET6_ADDRSTRLEN_MANAGED)); (void)addressLength; // Silence compiler warnings about unused variables on release mode (void)INET6_ADDRSTRLEN_MANAGED; // Silence compiler warnings about unused variables on release mode int32_t result; socklen_t len = UnsignedCast(stringLength); if (isIPv6) { sockaddr_in6 addr = {.sin6_scope_id = scope}; ConvertByteArrayToV6SockAddrIn(addr, address, addressLength); result = getnameinfo(reinterpret_cast<const sockaddr*>(&addr), sizeof(sockaddr_in6), reinterpret_cast<char*>(string), len, nullptr, 0, NI_NUMERICHOST); } else {
// Because RLIM_INFINITY is different per-platform, use the max value of a uint64 (which is RLIM_INFINITY on Ubuntu) // to signify RLIM_INIFINITY; on OS X, where RLIM_INFINITY is slightly lower, we'll translate it to the correct value // here. static uint64_t ConvertFromNativeRLimitInfinityToManagedIfNecessary(rlim_t value) { if (value == RLIM_INFINITY) return UINT64_MAX; return UnsignedCast(value); }
extern "C" void SystemNative_GetControlCharacters( int32_t* controlCharacterNames, uint8_t* controlCharacterValues, int32_t controlCharacterLength) { assert(controlCharacterNames != nullptr); assert(controlCharacterValues != nullptr); assert(controlCharacterLength >= 0); memset(controlCharacterValues, 0, sizeof(uint8_t) * UnsignedCast(controlCharacterLength)); #if HAVE_TCGETATTR struct termios newtermios = {}; if (tcgetattr(STDIN_FILENO, &newtermios) >= 0) { for (int i = 0; i < controlCharacterLength; i++) { int name = TranslatePalControlCharacterName(controlCharacterNames[i]); if (name >= 0) { controlCharacterValues[i] = newtermios.c_cc[name]; } } } #endif }
extern "C" int32_t ReadStdinUnbuffered(void* buffer, int32_t bufferSize) { assert(buffer != nullptr || bufferSize == 0); assert(bufferSize >= 0); if (bufferSize < 0) { errno = EINVAL; return -1; } #if HAVE_TCGETATTR && HAVE_TCSETATTR && HAVE_ECHO && HAVE_ICANON && HAVE_TCSANOW struct termios oldtermios = {}; struct termios newtermios = {}; if (tcgetattr(STDIN_FILENO, &oldtermios) < 0) return -1; newtermios = oldtermios; newtermios.c_lflag &= static_cast<uint32_t>(~(ECHO | ICANON)); newtermios.c_cc[VMIN] = 1; newtermios.c_cc[VTIME] = 0; if (tcsetattr(STDIN_FILENO, TCSANOW, &newtermios) < 0) return -1; ssize_t count = read(STDIN_FILENO, buffer, UnsignedCast(bufferSize)); tcsetattr(STDIN_FILENO, TCSANOW, &oldtermios); return static_cast<int32_t>(count); #else errno = ENOTSUP; return -1; #endif }
extern "C" int32_t CryptoNative_RsaSign(int32_t type, const uint8_t* m, int32_t mlen, uint8_t* sigret, int32_t* siglen, RSA* rsa) { if (siglen == nullptr) { assert(false); return 0; } *siglen = 0; if (HasNoPrivateKey(rsa)) { ERR_PUT_error(ERR_LIB_RSA, RSA_F_RSA_SIGN, RSA_R_VALUE_MISSING, __FILE__, __LINE__); return 0; } // Shared pointer to the metadata about the message digest algorithm const EVP_MD* digest = EVP_get_digestbynid(type); // If the digest itself isn't known then RSA_R_UNKNOWN_ALGORITHM_TYPE will get reported, but // we have to check that the digest size matches what we expect. if (digest != nullptr && mlen != EVP_MD_size(digest)) { ERR_PUT_error(ERR_LIB_RSA, RSA_F_RSA_SIGN, RSA_R_INVALID_MESSAGE_LENGTH, __FILE__, __LINE__); return 0; } unsigned int unsignedSigLen = 0; int32_t ret = RSA_sign(type, m, UnsignedCast(mlen), sigret, &unsignedSigLen, rsa); assert(unsignedSigLen <= INT32_MAX); *siglen = static_cast<int32_t>(unsignedSigLen); return ret; }
extern "C" void SystemNative_ReadEvents(int32_t sock, NetworkChangeEvent onNetworkChange) { char buffer[4096]; iovec iov = {buffer, sizeof(buffer)}; sockaddr_nl sanl; msghdr msg = { .msg_name = reinterpret_cast<void*>(&sanl), .msg_namelen = sizeof(sockaddr_nl), .msg_iov = &iov, .msg_iovlen = 1 }; ssize_t len; while (CheckInterrupted(len = recvmsg(sock, &msg, 0))); if (len == -1) { // Probably means the socket has been closed. return; } for (nlmsghdr* hdr = reinterpret_cast<nlmsghdr*>(buffer); NLMSG_OK(hdr, UnsignedCast(len)); NLMSG_NEXT(hdr, len)) { switch (hdr->nlmsg_type) { case NLMSG_DONE: return; // End of a multi-part message; stop reading. case NLMSG_ERROR: return; case RTM_NEWADDR: onNetworkChange(sock, NetworkChangeKind::AddressAdded); break; case RTM_DELADDR: onNetworkChange(sock, NetworkChangeKind::AddressRemoved); break; case RTM_NEWLINK: onNetworkChange(sock, ReadNewLinkMessage(hdr)); break; case RTM_DELLINK: onNetworkChange(sock, NetworkChangeKind::LinkRemoved); break; default: break; } } } NetworkChangeKind ReadNewLinkMessage(nlmsghdr* hdr) { assert(hdr != nullptr); ifinfomsg* ifimsg; ifimsg = reinterpret_cast<ifinfomsg*>(NLMSG_DATA(hdr)); if (ifimsg->ifi_family == AF_INET) { if ((ifimsg->ifi_flags & IFF_UP) != 0) { return NetworkChangeKind::LinkAdded; } } return NetworkChangeKind::None; }
static int IpStringToAddressHelper(const uint8_t* address, const uint8_t* port, bool isIPv6, addrinfo*& info) { assert(address != nullptr); addrinfo hint = {.ai_family = isIPv6 ? AF_INET6 : AF_INET, .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV}; info = nullptr; return getaddrinfo(reinterpret_cast<const char*>(address), reinterpret_cast<const char*>(port), &hint, &info); } static void ConvertByteArrayToIn6Addr(in6_addr& addr, const uint8_t* buffer, int32_t bufferLength) { #if HAVE_IN6_U assert(bufferLength == ARRAY_SIZE(addr.__in6_u.__u6_addr8)); memcpy(addr.__in6_u.__u6_addr8, buffer, UnsignedCast(bufferLength)); #else assert(bufferLength == ARRAY_SIZE(addr.__u6_addr.__u6_addr8)); memcpy(addr.__u6_addr.__u6_addr8, buffer, UnsignedCast(bufferLength)); #endif }
extern "C" char* SystemNative_GetCwd(char* buffer, int32_t bufferSize) { assert(bufferSize >= 0); if (bufferSize < 0) { errno = EINVAL; return nullptr; } return getcwd(buffer, UnsignedCast(bufferSize)); }
extern "C" int32_t EcDsaSign(const uint8_t* dgst, int32_t dgstlen, uint8_t* sig, int32_t* siglen, EC_KEY* key) { if (!siglen) { return 0; } unsigned int unsignedSigLength = UnsignedCast(*siglen); int ret = ECDSA_sign(0, dgst, dgstlen, sig, &unsignedSigLength, key); *siglen = SignedCast(unsignedSigLength); return ret; }
extern "C" int32_t CryptoNative_HmacUpdate(HMAC_CTX* ctx, const uint8_t* data, int32_t len) { assert(ctx != nullptr); assert(data != nullptr || len == 0); assert(len >= 0); if (len < 0) { return 0; } return HMAC_Update(ctx, data, UnsignedCast(len)); }
extern "C" const char* SystemNative_StrErrorR(int32_t platformErrno, char* buffer, int32_t bufferSize) { assert(buffer != nullptr); assert(bufferSize > 0); if (bufferSize < 0) return nullptr; // Note that we must use strerror_r because plain strerror is not // thread-safe. // // However, there are two versions of strerror_r: // - GNU: char* strerror_r(int, char*, size_t); // - POSIX: int strerror_r(int, char*, size_t); // // The former may or may not use the supplied buffer, and returns // the error message string. The latter stores the error message // string into the supplied buffer and returns an error code. #if HAVE_GNU_STRERROR_R const char* message = strerror_r(platformErrno, buffer, UnsignedCast(bufferSize)); assert(message != nullptr); return message; #else int error = strerror_r(platformErrno, buffer, UnsignedCast(bufferSize)); if (error == ERANGE) { // Buffer is too small to hold the entire message, but has // still been filled to the extent possible and null-terminated. return nullptr; } // The only other valid error codes are 0 for success or EINVAL for // an unkown error, but in the latter case a reasonable string (e.g // "Unknown error: 0x123") is returned. assert(error == 0 || error == EINVAL); return buffer; #endif }
extern "C" int32_t Write(int32_t fd, const void* buffer, int32_t bufferSize) { assert(buffer != nullptr || bufferSize == 0); assert(bufferSize >= 0); if (bufferSize < 0) { errno = ERANGE; return -1; } ssize_t count = write(fd, buffer, UnsignedCast(bufferSize)); assert(count >= -1 && count <= bufferSize); return static_cast<int32_t>(count); }
extern "C" int32_t Read(int32_t fd, void* buffer, int32_t bufferSize) { assert(buffer != nullptr || bufferSize == 0); assert(bufferSize >= 0); if (bufferSize < 0) { errno = EINVAL; return -1; } ssize_t count = read(fd, buffer, UnsignedCast(bufferSize)); assert(count >= -1 && count <= bufferSize); return static_cast<int32_t>(count); }
extern "C" int32_t SystemNative_ReadStdinUnbuffered(void* buffer, int32_t bufferSize) { assert(buffer != nullptr || bufferSize == 0); assert(bufferSize >= 0); if (bufferSize < 0) { errno = EINVAL; return -1; } ssize_t count; while (CheckInterrupted(count = read(STDIN_FILENO, buffer, UnsignedCast(bufferSize)))); return static_cast<int32_t>(count); }
extern "C" int32_t SystemNative_Write(intptr_t fd, const void* buffer, int32_t bufferSize) { assert(buffer != nullptr || bufferSize == 0); assert(bufferSize >= 0); if (bufferSize < 0) { errno = ERANGE; return -1; } ssize_t count; while (CheckInterrupted(count = write(ToFileDescriptor(fd), buffer, UnsignedCast(bufferSize)))); assert(count >= -1 && count <= bufferSize); return static_cast<int32_t>(count); }
extern "C" int32_t CryptoNative_HmacFinal(HMAC_CTX* ctx, uint8_t* md, int32_t* len) { assert(ctx != nullptr); assert(len != nullptr); assert(md != nullptr || *len == 0); assert(*len >= 0); if (len == nullptr || *len < 0) { return 0; } unsigned int unsignedLen = UnsignedCast(*len); int ret = HMAC_Final(ctx, md, &unsignedLen); *len = SignedCast(unsignedLen); return ret; }
extern "C" int32_t GetPwUidR(uint32_t uid, Passwd* pwd, char* buf, int32_t buflen) { assert(pwd != nullptr); assert(buf != nullptr); assert(buflen >= 0); if (buflen < 0) return EINVAL; struct passwd nativePwd; struct passwd* result; int error = getpwuid_r(uid, &nativePwd, buf, UnsignedCast(buflen), &result); // positive error number returned -> failure other than entry-not-found if (error != 0) { assert(error > 0); *pwd = {}; // managed out param must be initialized return error; } // 0 returned with null result -> entry-not-found if (result == nullptr) { *pwd = {}; // managed out param must be initialized return -1; // shim convention for entry-not-found } // 0 returned with non-null result (guaranteed to be set to pwd arg) -> success assert(result == &nativePwd); pwd->Name = nativePwd.pw_name; pwd->Password = nativePwd.pw_passwd; pwd->UserId = nativePwd.pw_uid; pwd->GroupId = nativePwd.pw_gid; pwd->UserInfo = nativePwd.pw_gecos; pwd->HomeDirectory = nativePwd.pw_dir; pwd->Shell = nativePwd.pw_shell; return 0; }
extern "C" int32_t SystemNative_CopyFile(intptr_t sourceFd, intptr_t destinationFd) { int inFd = ToFileDescriptor(sourceFd); int outFd = ToFileDescriptor(destinationFd); #if HAVE_FCOPYFILE // If fcopyfile is available (OS X), try to use it, as the whole copy // can be performed in the kernel, without lots of unnecessary copying. // Copy data and metadata. return fcopyfile(inFd, outFd, nullptr, COPYFILE_ALL) == 0 ? 0 : -1; #else // Get the stats on the source file. int ret; struct stat_ sourceStat; bool copied = false; #if HAVE_SENDFILE // If sendfile is available (Linux), try to use it, as the whole copy // can be performed in the kernel, without lots of unnecessary copying. while (CheckInterrupted(ret = fstat_(inFd, &sourceStat))); if (ret != 0) { return -1; } // We use `auto' here to adapt the type of `size' depending on the running platform. // On 32-bit, if you use 64-bit offsets, the last argument of `sendfile' will be a // `size_t' a 32-bit integer while the `st_size' field of the stat structure will be off64_t. // So `size' will have to be `uint64_t'. In all other cases, it will be `size_t'. auto size = UnsignedCast(sourceStat.st_size); // Note that per man page for large files, you have to iterate until the // whole file is copied (Linux has a limit of 0x7ffff000 bytes copied). while (size > 0) { ssize_t sent = sendfile(outFd, inFd, nullptr, (size >= SSIZE_MAX ? SSIZE_MAX : static_cast<size_t>(size))); if (sent < 0) { if (errno != EINVAL && errno != ENOSYS) { return -1; } else { break; } } else { assert(UnsignedCast(sent) <= size); size -= UnsignedCast(sent); } } if (size == 0) { copied = true; } // sendfile couldn't be used; fall back to a manual copy below. This could happen // if we're on an old kernel, for example, where sendfile could only be used // with sockets and not regular files. #endif // HAVE_SENDFILE // Manually read all data from the source and write it to the destination. if (!copied && CopyFile_ReadWrite(inFd, outFd) != 0) { return -1; } // Now that the data from the file has been copied, copy over metadata // from the source file. First copy the file times. while (CheckInterrupted(ret = fstat_(inFd, &sourceStat))); if (ret == 0) { struct timeval origTimes[2]; origTimes[0].tv_sec = sourceStat.st_atime; origTimes[0].tv_usec = 0; origTimes[1].tv_sec = sourceStat.st_mtime; origTimes[1].tv_usec = 0; while (CheckInterrupted(ret = futimes(outFd, origTimes))); } if (ret != 0) { return -1; } // Then copy permissions. while (CheckInterrupted(ret = fchmod(outFd, sourceStat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)))); if (ret != 0) { return -1; } return 0; #endif // HAVE_FCOPYFILE }
extern "C" int32_t CryptoNative_RsaVerify(int32_t type, const uint8_t* m, int32_t mlen, uint8_t* sigbuf, int32_t siglen, RSA* rsa) { return RSA_verify(type, m, UnsignedCast(mlen), sigbuf, UnsignedCast(siglen), rsa); }