Add getservbyname and getservbyport (#204)

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.

This change also adds fixes for the gethostbyname function.
This commit is contained in:
Gautham 2021-07-05 05:01:42 +05:30 committed by GitHub
parent cfbd2afc19
commit 0ea2907730
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 412 additions and 25 deletions

View file

@ -16,12 +16,25 @@ struct hostent {
};
#define h_addr h_addr_list[0]
struct servent {
char *s_name; /* official service name */
char **s_aliases; /* alias list */
int s_port; /* port number (in network byte order) */
char *s_proto; /* protocol to use */
};
struct hostent *gethostent(void);
struct hostent *gethostbyname(const char *);
struct hostent *gethostbyaddr(const void *, socklen_t, int);
void sethostent(int);
void endhostent(void);
struct servent *getservent(void);
struct servent *getservbyname(const char *, const char *);
struct servent *getservbyport(int, const char *);
void setservent(int);
void endservent(void);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_DNS_ENT_H_ */

View file

@ -31,23 +31,24 @@
struct hostent *gethostbyaddr(const void *s_addr, socklen_t len, int type) {
static struct hostent *ptr1, he1;
static char h_name[DNS_NAME_MAX+1];
static char* h_aliases[1];
static char* h_addr_list[2];
static char h_addr_list0[4];
struct sockaddr_in addr;
char name[DNS_NAME_MAX + 1];
if (!ptr1) {
he1.h_name = NULL;
he1.h_name = h_name;
he1.h_aliases = (char **)malloc(sizeof(char *) * 1);
if (!he1.h_aliases) return NULL;
he1.h_aliases = h_aliases;
he1.h_aliases[0] = NULL;
he1.h_addrtype = AF_INET;
he1.h_length = 4;
he1.h_addr_list = (char **)malloc(sizeof(char *) * 2);
if (!he1.h_addr_list) return NULL;
he1.h_addr_list = h_addr_list;
he1.h_addr_list[0] = (char *)malloc(sizeof(uint32_t));
if (!he1.h_addr_list[0]) return NULL;
he1.h_addr_list[0] = h_addr_list0;
he1.h_addr_list[1] = NULL;
ptr1 = &he1;
@ -58,12 +59,10 @@ struct hostent *gethostbyaddr(const void *s_addr, socklen_t len, int type) {
addr.sin_port = 0;
addr.sin_addr.s_addr = *(uint32_t *)(s_addr);
if (getnameinfo((struct sockaddr *)&addr, sizeof(addr), name, sizeof(name),
NULL, 0, 0))
if (getnameinfo((struct sockaddr *)&addr, sizeof(addr), ptr1->h_name,
DNS_NAME_MAX, NULL, 0, 0))
return NULL;
if (ptr1->h_name) free(ptr1->h_name);
ptr1->h_name = strdup(name);
*((uint32_t *)ptr1->h_addr_list[0]) = (addr.sin_addr.s_addr);
return ptr1;

View file

@ -27,26 +27,28 @@
*/
#include "libc/dns/ent.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/af.h"
struct hostent *gethostbyname(const char *name) {
static struct hostent *ptr0, he0;
static char h_name[DNS_NAME_MAX + 1];
static char *h_aliases[1];
static char *h_addr_list[2];
static char h_addr_list0[4];
struct addrinfo *result = NULL;
if (!ptr0) {
he0.h_name = NULL;
he0.h_name = h_name;
he0.h_aliases = (char **)malloc(sizeof(char *) * 1);
if (!he0.h_aliases) return NULL;
he0.h_aliases = h_aliases;
he0.h_aliases[0] = NULL;
he0.h_addrtype = AF_INET;
he0.h_length = 4;
he0.h_addr_list = (char **)malloc(sizeof(char *) * 2);
if (!he0.h_addr_list) return NULL;
he0.h_addr_list = h_addr_list;
he0.h_addr_list[0] = (char *)malloc(sizeof(uint32_t));
if (!he0.h_addr_list[0]) return NULL;
he0.h_addr_list[0] = h_addr_list0;
he0.h_addr_list[1] = NULL;
ptr0 = &he0;
@ -54,12 +56,12 @@ struct hostent *gethostbyname(const char *name) {
if (getaddrinfo(name, NULL, NULL, &result) || result == NULL) return NULL;
if (ptr0->h_name) free(ptr0->h_name);
if (result->ai_canonname) {
ptr0->h_name = strdup(result->ai_canonname);
} else {
ptr0->h_name = strdup(name);
}
/* if getaddrinfo is successful, result->ai_canonname is non-NULL,
* (see newaddrinfo) but the string can still be empty */
if (result->ai_canonname[0])
memccpy(ptr0->h_name, result->ai_canonname, '\0', DNS_NAME_MAX);
else
memccpy(ptr0->h_name, name, '\0', DNS_NAME_MAX);
*((uint32_t *)ptr0->h_addr_list[0]) = (result->ai_addr4->sin_addr.s_addr);
/* TODO: if result has ai_next, fit multiple entries for h_addr_list */

65
libc/dns/getservbyname.c Normal file
View file

@ -0,0 +1,65 @@
/*-*- 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/ent.h"
#include "libc/dns/servicestxt.h"
#include "libc/mem/mem.h"
struct servent *getservbyname(const char *name, const char *proto) {
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) {
// localproto got alloc'd during the lookup?
if (!proto && localproto != proto) free(localproto);
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);
if (!proto && localproto != proto) free(localproto);
return ptr0;
}

63
libc/dns/getservbyport.c Normal file
View file

@ -0,0 +1,63 @@
/*-*- 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/ent.h"
#include "libc/dns/servicestxt.h"
struct servent *getservbyport(int port, const char *proto) {
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) {
// localproto got alloc'd during the lookup?
if (!proto && localproto != proto) free(localproto);
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);
if (!proto && localproto != proto) free(localproto);
return ptr1;
}

38
libc/dns/servent.c Normal file
View file

@ -0,0 +1,38 @@
/*-*- 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/ent.h"
struct servent *getservent(void) {
return NULL;
}
void setservent(int stayopen) {
}
void endservent(void) {
}

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[PATH_MAX];
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[PATH_MAX];
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_ */