[LINUX] Créez votre propre service de résolution de noms

introduction

Qu'est-ce qu'un service de résolution de noms?

Un ordinateur nommé «exemple» est-il connecté au réseau (LAN) que vous utilisez actuellement? Lors de la création d'une machine virtuelle temporaire, vous pouvez créer une machine avec un nom comme foo ou hoge, mais je ne pense pas que vous utilisez généralement ce nom d'ordinateur. De plus, y a-t-il une définition pour le nom "exemple" dans / etc / hosts? Par exemple, si elle indique «127.0.0.1 exemple», l'exécution de «exemple de ping» devrait renvoyer le résultat du ping de votre machine.

À partir de ce moment, il n'y a pas d'hôte nommé "exemple", il n'y a pas de définition de ce type dans / etc / hosts, et l'exécution de ping example aboutit à ping: unknown host example. Je continuerai en supposant qu'il sera affiché.

La recherche d'une adresse IP à partir d'un nom d'hôte s'appelle la résolution de noms. Des exemples typiques sont "fichier hosts" et "résolveur dns". Ce qu'il faut contacter est automatiquement déterminé. Il est fourni par une fonctionnalité appelée nsswitch (Name Service Switch). Jetons d'abord un œil à ce fichier de configuration. Essayez d'afficher / 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

Au milieu, il y a une définition de hosts:, qui commence par files. Il est suivi de «mdns4» ou «mdns4_minimal», puis de «dns». Si vous avez installé Samba, vous pouvez également avoir wins.

Le premier files spécifie de rechercher d'abord à partir de la définition / etc / hosts. Le système mdns peut ne pas être disponible selon la configuration du système d'exploitation, mais il semble qu'il existe de nombreux cas où il peut être utilisé en standard dans un système d'exploitation récent. Cela s'appelle mDNS (Multicast DNS), qui est une fonction de résolution de nom standard dans macOS. Vous pouvez l'utiliser sur Windows en installant "Bonjour pour Windows" fourni par Apple, et il semble que Windows 10 prend en charge mDNS en standard, donc s'il est défini correctement, ping [nom d'hôte] Si vous l'exécutez comme .local, la résolution du nom réussira et vous verrez le résultat du ping. Le «dns» final est une spécification qui interroge le serveur DNS pour la résolution de noms Internet.

Si le nom d'hôte et l'adresse IP sont prédéterminés, il est plus simple et plus courant de les définir dans / etc / hosts. Si vous voulez effectuer une résolution de nom dynamiquement par un programme utilisant un format de fichier autre que les hôtes ou une base de données, vous pouvez créer votre propre bibliothèque de résolution de nom et l'enregistrer dans nsswitch.conf comme vous le souhaitez.

faire

Dans cet article, nous développerons une bibliothèque de résolution de noms utilisant le langage C. L'entité est un objet partagé.

Il n'est pas exagéré de dire que la fonction de résolution de noms est la base du fonctionnement du système d'exploitation, de sorte que les privilèges root sont nécessaires pour l'intégrer dans le système d'exploitation, et s'il y a un défaut dans le programme, cela peut être une faille de sécurité sérieuse dans certains cas. Soyez prudent là-bas.

Code source

L'ensemble du programme, y compris Makefile, est placé dans le référentiel suivant.

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

Il n'y a que deux fonctions dans un fichier source en langage C. La deuxième fonction appelle uniquement la première fonction, il n'y a donc en fait qu'une seule fonction.

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);
	}
}

Comment ça fonctionne

Tout ce que nous faisons, c'est que lorsque le nom de la requête est "exemple", il renvoie "127.0.0.1" comme résultat, c'est tout. À première vue, le processus de stockage des résultats dans une structure propre à nsswitch semble gênant, mais cette partie n'est qu'une farce complète des réalisations de nos prédécesseurs. Le programme original auquel j'ai fait référence est this.

compiler

Compiler.

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  

Je compile main.c pour créer libnss_example.so.2. Puisqu'il s'agit d'un objet partagé, il a l'extension «.so». Considérez le dernier «.2» comme quelque chose comme une version d'API. À part cela, cela ne fonctionnera pas, alors assurez-vous d'ajouter ".2" à la fin.

Installation

Puis installez-le.

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

Je le copie simplement dans / usr / lib /.

Changer le fichier de paramètres

Modifiez / etc / nsswitch.conf. Ajoutez ʻexample à la fin de la définition de hosts: ʻet vous avez terminé.

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

je le ferai

Exécutez un ping et vérifiez que le résultat est renvoyé.

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

Si vous le modifiez vous-même

Si vous voulez créer votre propre bibliothèque de résolution de noms, par exemple libnss_hogehoge.so.2 au lieu de libnss_example.so.2, tous les exemples sont contenus dans main.c et Makefile Remplacez par hogehoge. Les deux noms de fonction sont également _nss_hogehoge_gethostbyname_r et _nss_hogehoge_gethostbyname2_r, respectivement. Ensuite, s'il y a une adresse IP pour répondre au nom reçu par la fonction, définissez l'adresse sur struct in_addr addr, écrivez-la dans la structure et renvoyez NSS_STATUS_SUCCESS.

Après la compilation et l'installation, ajoutez le nom de la bibliothèque nouvellement créée à la définition hosts: dans / etc / nsswitch.conf.

Recommended Posts

Créez votre propre service de résolution de noms
Créez votre propre exception
Créez votre propre middleware Django
[Django] Créez votre propre page d'erreur 403, 404, 500
Créez vos propres commandes Linux en Python
[LLDB] Créez votre propre commande avec Python
Créez votre propre serveur DNS avec Twisted
Créez votre propre valeur composite avec SQLAlchemy
Créez une roue de votre propre module OpenCV
Mémo pour créer votre propre Box avec le Python de Pepper
Créez votre propre Big Data en Python pour validation
Créez votre propre stéréogramme aléatoire (RDS) en Python.
[Blender x Python] Créez votre propre fonction et résumé jusqu'à présent
Apprentissage par renforcement 23 Créez et utilisez votre propre module avec Colaboratory
Créez votre propre classe de structure graphique et son dessin avec python
Essayez docker: créez votre propre image de conteneur pour une application Web Python
Créez votre propre plateforme IoT en utilisant raspberrypi et ESP32 (partie 1)