[LINUX] Create your own name resolution service

Introduction

What is a name resolution service?

Is a PC named "example" connected to the network (LAN) you are using now? When you create a virtual machine for temporary use, you may build a machine with a name like foo or hoge, but I don't think you usually use that computer name. Also, is there a definition for the name "example" in / etc / hosts? For example, if it says 127.0.0.1 example, running ping example should return the ping result from your machine.

From here on, there is no host named "example", there is no such definition in / etc / hosts, and running ping example will result in ping: unknown host example. I will proceed with the assumption that it will be displayed.

Searching for an IP address from a host name is called name resolution. Typical examples are "hosts file" and "dns resolver". What to contact is automatically determined. It is provided by a feature called nsswitch (Name Service Switch). First, let's take a look at this configuration file. Try to display /etc/nsswitch.conf.

soramimi@alice:~$ cat /etc/nsswitch.conf 
# /etc/nsswitch.conf
#
# Example configuration of GNU Name Service Switch functionality.
# If you have the `glibc-doc-reference' and `info' packages installed, try:
# `info libc "Name Service Switch"' for information about this file.

passwd:         compat
group:          compat
shadow:         compat
gshadow:        files

hosts:          files mdns4_minimal [NOTFOUND=return] dns
networks:       files

protocols:      db files
services:       db files
ethers:         db files
rpc:            db files

netgroup:       nis

In the middle, there is a definition of hosts:, which starts with files. It is followed by mdns4 or mdns4_minimal, and then dns. If you have Samba installed, you may also have wins.

The leading files specifies to search from the / etc / hosts definition first. The mdns system may not be available depending on the OS configuration, but it seems that there are many cases where it can be used as standard in recent OS. This is called mDNS (Multicast DNS), which is a standard name resolution function in macOS. Even on Windows, you can use it by installing "Bonjour for Windows" provided by Apple, and it seems that Windows 10 supports mDNS as standard, so if it is set correctly, ping [host name] If you run it like .local, the name resolution will be successful and you will see the result of the ping. The final dns is a specification that queries the DNS server for Internet name resolution.

If the host name and IP address are predetermined, it is easiest and most common to define them in / etc / hosts. If you want to perform name resolution dynamically by a program using a file format other than hosts or a database, you can create your own name resolution library and register it in nsswitch.conf as you like.

make

In this article, we will develop a name resolution library using C language. The entity is a shared object.

It is no exaggeration to say that the name resolution function is the basis of OS operation, so root privileges are required to incorporate it into the OS, and if there is a defect in the program, it can be a serious security hole in some cases. Please be careful there.

Source code

The entire program including the Makefile is placed in the following repository.

https://github.com/soramimi/libnss-example

There are only two functions in a single C source file. The second function only calls the first function, so there is effectively only one function.

main.c


#include <string.h>
#include <nss.h>
#include <netdb.h>
#include <stdlib.h>
#include <errno.h>
#include <arpa/inet.h>

#define ALIGN(idx) do { \
	if (idx % sizeof(void*)) \
	idx += (sizeof(void*) - idx % sizeof(void*)); /* Align on 32 bit boundary */ \
	} while(0)

enum nss_status _nss_example_gethostbyname_r(const char *name, struct hostent *result, char *buffer, size_t buflen, int *errnop, int *h_errnop)
{
	size_t idx, astart;

	if (strcmp(name, "example") == 0) {
		char const *host = "127.0.0.1";

		*(char**)buffer = NULL;
		result->h_aliases = (char**) buffer;
		idx = sizeof(char*);

		strcpy(buffer + idx, name);
		result->h_name = buffer + idx;
		idx += strlen(name) + 1;
		ALIGN(idx);

		result->h_addrtype = AF_INET;
		result->h_length = sizeof(uint32_t);

		struct in_addr addr;

		inet_pton(AF_INET, host, &addr);

		astart = idx;
		memcpy(buffer+astart, &addr.s_addr, sizeof(uint32_t));
		idx += sizeof(uint32_t);

		result->h_addr_list = (char**)(buffer + idx);
		result->h_addr_list[0] = buffer + astart;
		result->h_addr_list[1] = NULL;

		return NSS_STATUS_SUCCESS;
	}

	*errnop = EINVAL;
	*h_errnop = NO_RECOVERY;
	return NSS_STATUS_UNAVAIL;

}

enum nss_status _nss_example_gethostbyname2_r(const char *name, int af, struct hostent *result, char *buffer, size_t buflen, int *errnop, int *h_errnop)
{
	if (af != AF_INET) {
		*errnop = EAGAIN;
		*h_errnop = NO_RECOVERY;
		return NSS_STATUS_TRYAGAIN;
	} else {
		return _nss_example_gethostbyname_r(name, result, buffer, buflen, errnop, h_errnop);
	}
}

How it works

All I'm doing is that when the name of the query is "example", it returns "127.0.0.1" as a result, that's it. At first glance, the process of storing the results in a structure peculiar to nsswitch seems to be troublesome, but this part is just a plagiarism of the achievements of our predecessors. The original program I referred to is this.

compile

Compile.

soramimi@alice:~/develop/libnss-example$ make
gcc   -g -O2 -Wall -Wpointer-arith -fPIC -c -o main.o main.c
gcc  -g -O2 -Wall -Wpointer-arith -shared -Wl,-soname,libnss_example.so.2 -Wl,-z,defs -o libnss_example.so.2 main.o  

I am compiling main.c to create libnss_example.so.2. Since it is a shared object, it has the extension .so. Think of the last .2 as something like an API version. Other than this, it will not work, so be sure to add .2 at the end.

Installation

Then install it.

soramimi@alice:~/develop/libnss-example$ sudo make install
install -m755 -d /usr/lib/
install -m644 libnss_example.so.2 /usr/lib/libnss_example.so.2

I'm just copying it into / usr / lib /.

Change configuration file

Edit /etc/nsswitch.conf. Add ʻexample to the end of the hosts: `definition and you're done.

...
hosts:          files mdns4_minimal [NOTFOUND=return] dns example
...

I will do it

Ping and verify that the results are returned.

soramimi@alice:~$ ping example
PING example (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.034 ms
64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.033 ms
64 bytes from localhost (127.0.0.1): icmp_seq=3 ttl=64 time=0.029 ms

If you modify it for yourself

If you want to create your own name resolution library, for example libnss_hogehoge.so.2 instead of libnss_example.so.2, all ʻexamples contained in main.candMakefileReplace withhogehoge. The two function names are also _nss_hogehoge_gethostbyname_r and _nss_hogehoge_gethostbyname2_r, respectively. Then, if there is an IP address to reply to name received by the function, set the address in struct in_addr addr, write it to the structure, and return NSS_STATUS_SUCCESS`.

After compiling and installing, add the name of the newly created library to the hosts: definition in /etc/nsswitch.conf.

Recommended Posts

Create your own name resolution service
Create your own exception
Create your own Django middleware
How to create your own Transform
[Django] Create your own 403, 404, 500 error pages
Create your own Linux commands in Python
[LLDB] Create your own command in Python
Create your own DNS server with Twisted
Create your own Composite Value with SQLAlchemy
Create a wheel of your own OpenCV module
Memo to create your own Box with Pepper's Python
Create your own Big Data in Python for validation
Create your own Random Dot Stereogram (RDS) in Python.
[Blender × Python] Create your own function & summary so far
Reinforcement learning 23 Create and use your own module with Colaboratory
Create your own graph structure class and its drawing in python
Try docker: Create your own container image for your Python web app
Create your own IoT platform using raspberry pi and ESP32 (Part 1)