implement getservbyname and getservbyport

getservbyname and getservbyport have implementation similar to that of
gethostbyname and gethostbyaddr. However, there is no struct ServicesTxt
like struct HostsTxt.

For every call of getservbyname/getservbyport the lookup of
/etc/services is done by opening the file and parsing each line
one-by-one. This is slow, but the implementation is simple, and the file
parsing is similar to ParseHostsTxt.
This commit is contained in:
ahgamut 2021-07-03 19:51:43 +05:30
parent 5e8d035302
commit 8c9750fb30
5 changed files with 269 additions and 3 deletions

View file

@ -19,7 +19,7 @@ struct hostent {
struct servent {
char *s_name; /* official service name */
char **s_aliases; /* alias list */
int s_port; /* port number */
int s_port; /* port number (in network byte order) */
char *s_proto; /* protocol to use */
};

View file

@ -26,7 +26,37 @@
*/
#include "libc/dns/ent.h"
#include "libc/dns/servicestxt.h"
#include "libc/mem/mem.h"
struct servent *getservbyname(const char *name, const char *proto) {
return NULL;
static struct servent *ptr0, se0;
char *localproto = proto;
int p;
if (!ptr0) {
se0.s_name = NULL;
se0.s_aliases = (char **)malloc(sizeof(char *) * 1);
if (!se0.s_aliases) return NULL;
se0.s_aliases[0] = NULL;
se0.s_port = 0;
se0.s_proto = NULL;
ptr0 = &se0;
}
p = LookupServicesByName(name, &localproto);
if (p == -1) return NULL;
ptr0->s_port = p;
if (ptr0->s_name) free(ptr0->s_name);
ptr0->s_name = strdup(name);
if (ptr0->s_proto) free(ptr0->s_proto);
ptr0->s_proto = strdup(localproto);
// localproto got alloc'd during the lookup
if (!proto && localproto != proto) free(localproto);
return ptr0;
}

View file

@ -26,7 +26,36 @@
*/
#include "libc/dns/ent.h"
#include "libc/dns/servicestxt.h"
struct servent *getservbyport(int port, const char *proto) {
return NULL;
static struct servent *ptr1, se1;
char name[DNS_NAME_MAX];
char *localproto = proto;
if (!ptr1) {
se1.s_name = NULL;
se1.s_aliases = (char **)malloc(sizeof(char *) * 1);
if (!se1.s_aliases) return NULL;
se1.s_aliases[0] = NULL;
se1.s_port = 0;
se1.s_proto = NULL;
ptr1 = &se1;
}
if (LookupServicesByPort(port, &localproto, name, sizeof(name)) == -1)
return NULL;
ptr1->s_port = port;
if (ptr1->s_name) free(ptr1->s_name);
ptr1->s_name = strdup(name);
if (ptr1->s_proto) free(ptr1->s_proto);
ptr1->s_proto = strdup(localproto);
// localproto got alloc'd during the lookup
if (!proto && localproto != proto) free(localproto);
return ptr1;
}

190
libc/dns/servicestxt.c Normal file
View file

@ -0,0 +1,190 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#include "libc/dns/servicestxt.h"
#include "libc/bits/safemacros.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/fmt/fmt.h"
#include "libc/macros.internal.h"
#include "libc/mem/mem.h"
#include "libc/nt/systeminfo.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
static textwindows noinline char *GetNtServicesTxtPath(char *pathbuf,
uint32_t size) {
const char *const kWinHostsPath = "\\drivers\\etc\\services";
uint32_t len = GetSystemDirectoryA(&pathbuf[0], size);
if (len && len + strlen(kWinHostsPath) + 1 < size) {
if (pathbuf[len] == '\\') pathbuf[len--] = '\0';
memcpy(&pathbuf[len], kWinHostsPath, strlen(kWinHostsPath) + 1);
return &pathbuf[0];
} else {
return NULL;
}
}
/**
* Opens and searches /etc/services to find name for a given port.
*
* format of /etc/services is like this:
*
* # comment
* # NAME PORT/PROTOCOL ALIASES
*
* ftp 21/tcp
* fsp 21/udp fspd
* ssh 22/tcp
*
* @param servport is the port number (in network byte order)
* @param servproto is a pointer to a string (*servproto can be NULL)
* @param buf is a buffer to store the resulting name
* @param bufsize is the size of buf
* @returns 0 on success, -1 on error
*
* @note aliases are not read from the file.
*/
int LookupServicesByPort(const int servport, char **servproto, char *buf,
size_t bufsize) {
FILE *f;
char *line;
char pathbuf[512];
const char *path;
size_t linesize;
int count, found;
char *name, *port, *proto, *comment, *tok;
path = "/etc/services";
if (IsWindows()) {
path = firstnonnull(GetNtServicesTxtPath(pathbuf, ARRAYLEN(pathbuf)), path);
}
if (bufsize == 0 || !(f = fopen(path, "r"))) {
return -1;
}
line = NULL;
linesize = 0;
count = 0;
found = 0;
while (found == 0 && (getline(&line, &linesize, f)) != -1) {
if ((comment = strchr(line, '#'))) *comment = '\0';
name = strtok_r(line, " \t\r\n\v", &tok);
port = strtok_r(NULL, "/ \t\r\n\v", &tok);
if (name && port && servport == htons(atoi(port))) {
if (!(proto = strtok_r(NULL, " \t\r\n\v", &tok)))
continue;
else if (!servproto[0]) {
servproto[0] = strdup(proto);
strncpy(buf, name, bufsize);
found = 1;
} else if (strcmp(proto, servproto[0]) == 0) {
strncpy(buf, name, bufsize);
found = 1;
}
}
count++;
}
free(line);
if (ferror(f)) {
errno = ferror(f);
return -1;
}
fclose(f);
if (!found) return -1;
return 0;
}
/**
* Opens and searches /etc/services to find port for a given name.
*
* @param servname is a NULL-terminated string
* @param servproto is a pointer to a string (*servproto can be NULL)
* @returns -1 on error, or
* positive port number (in network byte order)
*
* @note aliases are not read from the file.
* @see LookupServicesByPort
*/
int LookupServicesByName(const char *servname, char **servproto) {
FILE *f;
char *line;
char pathbuf[512];
const char *path;
size_t linesize;
int count, found, result;
char *name, *port, *proto, *comment, *tok;
path = "/etc/services";
if (IsWindows()) {
path = firstnonnull(GetNtServicesTxtPath(pathbuf, ARRAYLEN(pathbuf)), path);
}
if (!(f = fopen(path, "r"))) {
return -1;
}
line = NULL;
linesize = 0;
count = 0;
found = 0;
result = -1;
while (found == 0 && (getline(&line, &linesize, f)) != -1) {
if ((comment = strchr(line, '#'))) *comment = '\0';
name = strtok_r(line, " \t\r\n\v", &tok);
port = strtok_r(NULL, "/ \t\r\n\v", &tok);
if (name && port && strcmp(name, servname) == 0) {
if (!(proto = strtok_r(NULL, " \t\r\n\v", &tok)))
continue;
else if (!servproto[0]) {
servproto[0] = strdup(proto);
result = htons(atoi(port));
found = 1;
} else if (strcmp(proto, servproto[0]) == 0) {
result = htons(atoi(port));
found = 1;
}
}
count++;
}
free(line);
if (ferror(f)) {
errno = ferror(f);
return -1;
}
fclose(f);
if (!found) return -1;
return result;
}

17
libc/dns/servicestxt.h Normal file
View file

@ -0,0 +1,17 @@
#ifndef COSMOPOLITAN_LIBC_DNS_SERVICESTXT_H_
#define COSMOPOLITAN_LIBC_DNS_SERVICESTXT_H_
#include "libc/sock/sock.h"
#include "libc/stdio/stdio.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
int LookupServicesByPort(const int, char **, char *, size_t)
paramsnonnull((2, 3));
int LookupServicesByName(const char *, char **) paramsnonnull((1, 2));
/* TODO: implement like struct HostsTxt? */
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_DNS_SERVICESTXT_H_ */