summaryrefslogtreecommitdiff
path: root/lib/mlibc/options/posix/generic/services.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/mlibc/options/posix/generic/services.cpp')
-rw-r--r--lib/mlibc/options/posix/generic/services.cpp222
1 files changed, 222 insertions, 0 deletions
diff --git a/lib/mlibc/options/posix/generic/services.cpp b/lib/mlibc/options/posix/generic/services.cpp
new file mode 100644
index 0000000..8ae0656
--- /dev/null
+++ b/lib/mlibc/options/posix/generic/services.cpp
@@ -0,0 +1,222 @@
+#include <mlibc/services.hpp>
+#include <netdb.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <mlibc/debug.hpp>
+
+namespace mlibc {
+
+static int parse_rest(service_buf &buf, char *end, int proto) {
+ if (!strncmp(end, "/udp", 4)) {
+ if (proto == IPPROTO_TCP && proto != -1)
+ return 0;
+ buf.protocol = IPPROTO_UDP;
+ buf.socktype = SOCK_DGRAM;
+ } else if (!strncmp(end, "/tcp", 4)) {
+ if (proto == IPPROTO_UDP && proto != -1)
+ return 0;
+ buf.protocol = IPPROTO_TCP;
+ buf.socktype = SOCK_STREAM;
+ } else {
+ return 0;
+ }
+
+ //TODO(geert): also parse aliases.
+
+ return 1;
+}
+
+static int lookup_serv_file_port(service_result &buf, int proto, int port) {
+ auto file = fopen(_PATH_SERVICES, "r");
+ if (!file) {
+ switch (errno) {
+ case ENOENT:
+ case ENOTDIR:
+ case EACCES:
+ return -EAI_SERVICE;
+ default:
+ return -EAI_SYSTEM;
+ }
+ }
+
+ char line_buf[129] = {0};
+ char *line = line_buf + 1;
+ while(fgets(line, 128, file)) {
+ int name_length = 0;
+ char *pos;
+ // easy way to handle comments, just move the end of the line
+ // to the beginning of the comment
+ if ((pos = strchr(line, '#'))) {
+ *pos++ = '\n';
+ *pos = '\0';
+ }
+
+ char *end = NULL;
+ for (pos = line; *pos; pos++) {
+ for (; isalpha(*pos); pos++);
+ int rport = strtoul(pos, &end, 10);
+ if (rport != port || rport > 65535) {
+ pos = end;
+ continue;
+ }
+
+ // We have found the port, time to rewind to the start
+ // of the line.
+ for (; pos[-1]; pos--)
+ if(!isspace(pos[-1]))
+ name_length++;
+ break;
+ }
+
+ if (!pos)
+ continue;
+
+ if (!name_length)
+ continue;
+
+ auto name = frg::string<MemoryAllocator>(pos, name_length,
+ getAllocator());
+
+ struct service_buf sbuf = {};
+ sbuf.port = port;
+ sbuf.name = std::move(name);
+ if (!parse_rest(sbuf, end, proto))
+ continue;
+ buf.push_back(std::move(sbuf));
+ }
+
+ fclose(file);
+ return buf.size();
+}
+
+static int lookup_serv_file_name(service_result &buf, const char *name,
+ int proto) {
+ auto file = fopen(_PATH_SERVICES, "r");
+ if (!file) {
+ switch (errno) {
+ case ENOENT:
+ case ENOTDIR:
+ case EACCES:
+ return -EAI_SERVICE;
+ default:
+ return -EAI_SYSTEM;
+ }
+ }
+
+ char line[128];
+ int name_length = strlen(name);
+ while(fgets(line, 128, file)) {
+ char *pos;
+ // easy way to handle comments, just move the end of the line
+ // to the beginning of the comment
+ if ((pos = strchr(line, '#'))) {
+ *pos++ = '\n';
+ *pos = '\0';
+ }
+
+ for (pos = line; (pos = strstr(pos, name)); pos++) {
+ // the name must start and end with a space
+ if (pos > line && !isspace(pos[-1]))
+ continue;
+ if (pos[name_length] && !isspace(pos[name_length]))
+ continue;
+ break;
+ }
+ if (!pos)
+ continue;
+
+ // Skip the name at the beginning of the line.
+ for(pos = line; *pos && !isspace(*pos); pos++)
+ ;
+
+ char *end = NULL;
+ int port = strtoul(pos, &end, 10);
+ if (port > 65535 || end == pos)
+ continue;
+
+ struct service_buf sbuf;
+ sbuf.port = port;
+ sbuf.name = frg::string<MemoryAllocator>(name, getAllocator());
+ if (!parse_rest(sbuf, end, proto))
+ continue;
+
+ buf.push_back(sbuf);
+
+ }
+
+ fclose(file);
+ return buf.size();
+}
+
+
+// This function returns a negative error code, since a positive
+// return code means success.
+int lookup_serv_by_name(service_result &buf, const char *name, int proto,
+ int socktype, int flags) {
+ switch(socktype) {
+ case SOCK_STREAM:
+ if (!proto)
+ proto = IPPROTO_TCP;
+ else if (proto != IPPROTO_TCP)
+ return -EAI_SERVICE;
+ break;
+ case SOCK_DGRAM:
+ if (!proto)
+ proto = IPPROTO_UDP;
+ else if (proto != IPPROTO_UDP)
+ return -EAI_SERVICE;
+ break;
+ case 0:
+ break;
+ default:
+ if (name)
+ return -EAI_SERVICE;
+ buf[0].port = 0;
+ buf[0].socktype = socktype;
+ buf[0].protocol = proto;
+ return 1;
+ }
+
+ char *end = nullptr;
+ unsigned int port = 0;
+ int count = 0;
+
+ if (name) {
+ if (!*name)
+ return -EAI_SERVICE;
+ port = strtoul(name, &end, 10);
+ }
+ // The end pointer is a null pointer so the name was a port
+ // or the name was not specified.
+ if (!end || !*end) {
+ if (proto != IPPROTO_UDP) {
+ buf[count].port = port;
+ buf[count].protocol = IPPROTO_TCP;
+ buf[count].socktype = SOCK_STREAM;
+ count++;
+ }
+ if (proto != IPPROTO_TCP) {
+ buf[count].port = port;
+ buf[count].protocol = IPPROTO_UDP;
+ buf[count].socktype = SOCK_DGRAM;
+ count++;
+ }
+ return count;
+ }
+
+ if (flags & AI_NUMERICSERV)
+ return -EAI_NONAME;
+
+ return lookup_serv_file_name(buf, name, proto);
+}
+
+int lookup_serv_by_port(service_result &buf, int proto, int port) {
+ return lookup_serv_file_port(buf, proto, port);
+}
+
+} // namespace mlibc