int connectToUnixServer(const StaticString &filename, const char *file, unsigned int line) { int fd = syscalls::socket(PF_UNIX, SOCK_STREAM, 0); if (fd == -1) { int e = errno; throw SystemException("Cannot create a Unix socket file descriptor", e); } FdGuard guard(fd, file, line, true); int ret; struct sockaddr_un addr; if (filename.size() > sizeof(addr.sun_path) - 1) { string message = "Cannot connect to Unix socket '"; message.append(filename.data(), filename.size()); message.append("': filename is too long."); throw RuntimeException(message); } addr.sun_family = AF_UNIX; memcpy(addr.sun_path, filename.c_str(), filename.size()); addr.sun_path[filename.size()] = '\0'; bool retry = true; int counter = 0; while (retry) { ret = syscalls::connect(fd, (const sockaddr *) &addr, sizeof(addr)); if (ret == -1) { #if defined(sun) || defined(__sun) /* Solaris has this nice kernel bug where connecting to * a newly created Unix socket which is obviously * connectable can cause an ECONNREFUSED. So we retry * in a loop. */ retry = errno == ECONNREFUSED; #else retry = false; #endif retry = retry && counter < 9; if (retry) { syscalls::usleep((useconds_t) (10000 * pow((double) 2, (double) counter))); counter++; } else { int e = errno; string message("Cannot connect to Unix socket '"); message.append(filename.toString()); message.append("'"); throw SystemException(message, e); } } else { guard.clear(); return fd; } } abort(); // Never reached. return -1; // Shut up compiler warning. }
string lookupSystemUsernameByUid(uid_t uid, const StaticString &fallbackFormat) { OsUser user; bool result; try { result = lookupSystemUserByUid(uid, user); } catch (const SystemException &) { result = false; } if (result && user.pwd.pw_name != NULL && user.pwd.pw_name[0] != '\0') { return user.pwd.pw_name; } else { // Null terminate fallback format string DynamicBuffer fallbackFormatNt(fallbackFormat.size() + 1); memcpy(fallbackFormatNt.data, fallbackFormat.data(), fallbackFormat.size()); fallbackFormatNt.data[fallbackFormat.size()] = '\0'; char buf[512]; snprintf(buf, sizeof(buf), fallbackFormatNt.data, (int) uid); buf[sizeof(buf) - 1] = '\0'; return buf; } }
string lookupSystemGroupnameByGid(gid_t gid, const StaticString &fallbackFormat) { OsGroup group; bool result; try { result = lookupSystemGroupByGid(gid, group); } catch (const SystemException &) { result = false; } if (result && group.grp.gr_name != NULL && group.grp.gr_name[0] != '\0') { return group.grp.gr_name; } else { // Null terminate fallback format string DynamicBuffer fallbackFormatNt(fallbackFormat.size() + 1); memcpy(fallbackFormatNt.data, fallbackFormat.data(), fallbackFormat.size()); fallbackFormatNt.data[fallbackFormat.size()] = '\0'; char buf[512]; snprintf(buf, sizeof(buf), fallbackFormatNt.data, (int) gid); buf[sizeof(buf) - 1] = '\0'; return buf; } }
static Numeric stringToSignedNumeric(const StaticString &str) { Numeric result = 0; string::size_type i = 0; const char *data = str.data(); bool minus = false; while (data[i] == ' ' && i < str.size()) { i++; } if (data[i] == '-') { minus = true; i++; } while (data[i] >= '0' && data[i] <= '9' && i < str.size()) { result *= 10; result += data[i] - '0'; i++; } if (minus) { return -result; } else { return result; } }
int createUnixServer(const StaticString &filename, unsigned int backlogSize, bool autoDelete, const char *file, unsigned int line) { struct sockaddr_un addr; int fd, ret; if (filename.size() > sizeof(addr.sun_path) - 1) { string message = "Cannot create Unix socket '"; message.append(filename.toString()); message.append("': filename is too long."); throw RuntimeException(message); } fd = syscalls::socket(PF_LOCAL, SOCK_STREAM, 0); if (fd == -1) { int e = errno; throw SystemException("Cannot create a Unix socket file descriptor", e); } FdGuard guard(fd, file, line, true); addr.sun_family = AF_LOCAL; strncpy(addr.sun_path, filename.c_str(), filename.size()); addr.sun_path[filename.size()] = '\0'; if (autoDelete) { do { ret = unlink(filename.c_str()); } while (ret == -1 && errno == EINTR); } ret = syscalls::bind(fd, (const struct sockaddr *) &addr, sizeof(addr)); if (ret == -1) { int e = errno; string message = "Cannot bind Unix socket '"; message.append(filename.toString()); message.append("'"); throw SystemException(message, e); } if (backlogSize == 0) { backlogSize = 1024; } ret = syscalls::listen(fd, backlogSize); if (ret == -1) { int e = errno; string message = "Cannot listen on Unix socket '"; message.append(filename.toString()); message.append("'"); safelyClose(fd, true); throw SystemException(message, e); } guard.clear(); return fd; }
void setupNonBlockingTcpSocket(NTCP_State &state, const StaticString &hostname, int port) { int ret; memset(&state.hints, 0, sizeof(state.hints)); state.hints.ai_family = PF_UNSPEC; state.hints.ai_socktype = SOCK_STREAM; ret = getaddrinfo(hostname.toString().c_str(), toString(port).c_str(), &state.hints, &state.res); if (ret != 0) { string message = "Cannot resolve IP address '"; message.append(hostname.data(), hostname.size()); message.append(":"); message.append(toString(port)); message.append("': "); message.append(gai_strerror(ret)); throw IOException(message); } state.fd = syscalls::socket(PF_INET, SOCK_STREAM, 0); if (state.fd == -1) { int e = errno; throw SystemException("Cannot create a TCP socket file descriptor", e); } state.hostname = hostname; state.port = port; setNonBlocking(state.fd); }
static Numeric hexToUnsignedNumeric(const StaticString &hex) { const char *pos = hex.data(); const char *end = hex.data() + hex.size(); Numeric result = 0; bool done = false; while (pos < end && !done) { char c = *pos; if (c >= '0' && c <= '9') { result *= 16; result += c - '0'; } else if (c >= 'a' && c <= 'f') { result *= 16; result += 10 + (c - 'a'); } else if (c >= 'A' && c <= 'F') { result *= 16; result += 10 + (c - 'A'); } else { done = true; } pos++; } return result; }
string parseUnixSocketAddress(const StaticString &address) { if (getSocketAddressType(address) != SAT_UNIX) { throw ArgumentException("Not a valid Unix socket address"); } return string(address.c_str() + sizeof("unix:") - 1, address.size() - sizeof("unix:") + 1); }
static Numeric stringToUnsignedNumeric(const StaticString &str) { Numeric result = 0; string::size_type i = 0; const char *data = str.data(); while (data[i] == ' ' && i < str.size()) { i++; } while (data[i] >= '0' && data[i] <= '9' && i < str.size()) { result *= 10; result += data[i] - '0'; i++; } return result; }
string cEscapeString(const StaticString &input) { string result; const char *current = input.c_str(); const char *end = current + input.size(); result.reserve(input.size()); while (current < end) { char c = *current; if (c >= 32 && c <= 126) { // Printable ASCII. result.append(1, c); } else { char buf[sizeof("\\xFF")]; switch (c) { case '\0': // Explicitly in hex format in order to avoid confusion // with any '0' characters that come after this byte. result.append("\\x00"); break; case '\t': result.append("\\t"); break; case '\n': result.append("\\n"); break; case '\r': result.append("\\r"); break; case '\e': result.append("\\e"); break; default: buf[0] = '\\'; buf[1] = 'x'; toHex(StaticString(current, 1), buf + 2, true); buf[4] = '\0'; result.append(buf, sizeof(buf) - 1); break; } } current++; } return result; }
void toHex(const StaticString &data, char *output, bool upperCase) { const char *data_buf = data.c_str(); string::size_type i; if (upperCase) { for (i = 0; i < data.size(); i++) { output[i * 2] = upcase_hex_chars[(unsigned char) data_buf[i] / 16]; output[i * 2 + 1] = upcase_hex_chars[(unsigned char) data_buf[i] % 16]; } } else { for (i = 0; i < data.size(); i++) { output[i * 2] = hex_chars[(unsigned char) data_buf[i] / 16]; output[i * 2 + 1] = hex_chars[(unsigned char) data_buf[i] % 16]; } } }
bool constantTimeCompare(const StaticString &a, const StaticString &b) { // http://blog.jasonmooberry.com/2010/10/constant-time-string-comparison/ // See also ActiveSupport::MessageVerifier#secure_compare. if (a.size() != b.size()) { return false; } else { const char *x = a.data(); const char *y = b.data(); const char *end = a.data() + a.size(); int result = 0; while (x < end) { result |= *x ^ *y; x++; y++; } return result == 0; } }
string strip(const StaticString &str) { const char *data = str.data(); const char *end = str.data() + str.size(); while (data < end && (*data == ' ' || *data == '\n' || *data == '\t')) { data++; } while (end > data && (end[-1] == ' ' || end[-1] == '\n' || end[-1] == '\t')) { end--; } return string(data, end - data); }
string escapeHTML(const StaticString &input) { string result; result.reserve((int) round(input.size() * 1.25)); const char *current = (const char *) input.c_str(); const char *end = current + input.size(); while (current < end) { char ch = *current; if (ch & 128) { // Multibyte UTF-8 character. const char *prev = current; utf8::advance(current, 1, end); result.append(prev, current - prev); } else { // ASCII character <= 127. if (ch == '<') { result.append("<"); } else if (ch == '>') { result.append(">"); } else if (ch == '&') { result.append("&"); } else if (ch == '"') { result.append("""); } else if (ch == '\'') { result.append("'"); } else if (ch >= 0x21 || ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t') { result.append(1, ch); } else { result.append("&#"); result.append(toString((int) ((unsigned char) ch))); result.append(";"); } current++; } } return result; }
bool lookupSystemGroupByName(const StaticString &name, OsGroup &result) { TRACE_POINT(); // Null terminate name DynamicBuffer ntName(name.size() + 1); memcpy(ntName.data, name.data(), name.size()); ntName.data[name.size()] = '\0'; int code; struct group *output = NULL; do { code = getgrnam_r(ntName.data, &result.grp, result.buffer.data, result.buffer.size, &output); } while (code == EINTR || code == EAGAIN); if (code == 0) { return output != NULL; } else { throw SystemException("Error looking up OS group account " + name, code); } }
ServerAddressType getSocketAddressType(const StaticString &address) { const char *data = address.c_str(); size_t len = address.size(); if (len > sizeof("unix:") - 1 && memcmp(data, "unix:", sizeof("unix:") - 1) == 0) { return SAT_UNIX; } else if (len > sizeof("tcp://") - 1 && memcmp(data, "tcp://", sizeof("tcp://") - 1) == 0) { return SAT_TCP; } else { return SAT_UNKNOWN; } }
static void _splitIncludeSep(const StaticString &str, char sep, vector<OutputString> &output) { output.clear(); if (!str.empty()) { string::size_type start, pos; start = 0; while ((pos = str.find(sep, start)) != string::npos) { output.push_back(str.substr(start, pos - start + 1)); start = pos + 1; } if (start != str.size()) { output.push_back(str.substr(start)); } } }
/** * Linux-only way to change OOM killer configuration for * current process. Requires root privileges, which we * should have. */ static void setOomScore(const StaticString &score) { if (score.empty()) { return; } FILE *f; OomFileType type; f = openOomAdjFile("w", type); if (f != NULL) { fwrite(score.data(), 1, score.size(), f); fclose(f); } }
void parseTcpSocketAddress(const StaticString &address, string &host, unsigned short &port) { if (getSocketAddressType(address) != SAT_TCP) { throw ArgumentException("Not a valid TCP socket address"); } vector<string> args; string begin(address.c_str() + sizeof("tcp://") - 1, address.size() - sizeof("tcp://") + 1); split(begin, ':', args); if (args.size() != 2) { throw ArgumentException("Not a valid TCP socket address"); } else { host = args[0]; port = atoi(args[1].c_str()); } }
void parseTcpSocketAddress(const StaticString &address, string &host, unsigned short &port) { if (getSocketAddressType(address) != SAT_TCP) { throw ArgumentException("Not a valid TCP socket address"); } StaticString hostAndPort(address.data() + sizeof("tcp://") - 1, address.size() - sizeof("tcp://") + 1); if (hostAndPort.empty()) { throw ArgumentException("Not a valid TCP socket address"); } if (hostAndPort[0] == '[') { // IPv6 address, e.g.: // [::1]:3000 const char *hostEnd = (const char *) memchr(hostAndPort.data(), ']', hostAndPort.size()); if (hostEnd == NULL || hostAndPort.size() <= string::size_type(hostEnd - hostAndPort.data()) + 3) { throw ArgumentException("Not a valid TCP socket address"); } const char *sep = hostEnd + 1; host.assign(hostAndPort.data() + 1, hostEnd - hostAndPort.data() - 1); port = stringToUint(StaticString( sep + 1, hostAndPort.data() + hostAndPort.size() - sep - 1 )); } else { // IPv4 address, e.g.: // 127.0.0.1:3000 const char *sep = (const char *) memchr(hostAndPort.data(), ':', hostAndPort.size()); if (sep == NULL || hostAndPort.size() <= string::size_type(sep - hostAndPort.data()) + 2) { throw ArgumentException("Not a valid TCP socket address"); } host.assign(hostAndPort.data(), sep - hostAndPort.data()); port = stringToUint(StaticString( sep + 1, hostAndPort.data() + hostAndPort.size() - sep - 1 )); } }
unsigned long long hexatriToULL(const StaticString &str) { unsigned long long result = 0; string::size_type i = 0; bool done = false; while (i < str.size() && !done) { char c = str[i]; if (c >= '0' && c <= '9') { result *= 36; result += c - '0'; } else if (c >= 'a' && c <= 'z') { result *= 36; result += 10 + (c - 'a'); } else if (c >= 'A' && c <= 'Z') { result *= 36; result += 10 + (c - 'A'); } else { done = true; } i++; } return result; }
string toHex(const StaticString &data) { string result(data.size() * 2, '\0'); toHex(data, (char *) result.data()); return result; }
void writeExact(int fd, const StaticString &data, unsigned long long *timeout) { const char * restrict data_ptr = data.data(); writeExact(fd, data_ptr, data.size(), timeout); }