When working across multiple machines in a small LAN environment, managing hostname resolution becomes challenging. The standard /etc/hosts
approach presents several limitations:
# Traditional /etc/hosts entry 192.168.1.10 server1 192.168.1.11 server2
- Requires root privileges to modify
- Changes affect all users on the system
- Synchronization across machines is cumbersome
Several approaches exist for per-user host resolution:
1. Custom DNS Resolution with getent
Create a ~/.hosts
file and modify your shell configuration:
# ~/.bashrc or ~/.zshrc addition resolve_host() { local host=$1 grep "^[0-9]" ~/.hosts | awk -v host="$host" '$2 == host {print $1}' } alias myping='ping $(resolve_host target)'
2. Using dnsmasq with Local Configuration
For more advanced setups, configure dnsmasq to read per-user hosts files:
# ~/.config/dnsmasq.conf addn-hosts=/home/user/.hosts
Then run a personal dnsmasq instance:
dnsmasq --no-daemon --conf-file=/home/user/.config/dnsmasq.conf
3. NetworkManager's connection-specific DNS
Modern Linux systems using NetworkManager support per-connection DNS:
# nmcli example nmcli connection modify eth0 ipv4.dns "192.168.1.1" nmcli connection modify eth0 +ipv4.dns-search "mydomain.lan"
For keeping hosts files in sync across machines, consider this rsync wrapper:
#!/bin/bash # sync-hosts.sh RSYNC_OPTS="-avz --delete" TARGETS=("user@machine1:/home/user/.hosts" "user@machine2:/home/user/.hosts") for target in "${TARGETS[@]}"; do rsync $RSYNC_OPTS ~/.hosts "$target" done
- Restrict .hosts file permissions:
chmod 600 ~/.hosts
- Validate IP addresses before syncing
- Consider using SSH keys for rsync transfers
Here's a complete example of the custom resolver approach:
# Sample ~/.hosts 192.168.1.100 nas.local 192.168.1.101 dev.local # ~/.bashrc additions export HOSTALIASES=~/.hosts function resolve() { [ -f ~/.hosts ] && getent hosts $1 || getent ahosts $1 | head -n1 | awk '{print $1}' } # Usage examples: ping $(resolve nas.local) ssh user@$(resolve dev.local)
While /etc/hosts
provides system-wide hostname resolution, it lacks user-specific flexibility. In LAN environments where machines frequently disconnect (making DNS servers impractical), we need a more dynamic solution.
Create a ~/.hosts
file with the same format as /etc/hosts
:
127.0.0.1 localhost 192.168.1.10 fileserver.lan 192.168.1.20 printer.lan
Method 1: Custom Resolver via LD_PRELOAD
Create a shared library to intercept DNS lookups:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <netdb.h> struct hostent *gethostbyname(const char *name) { FILE *fp = fopen(".hosts", "r"); if (fp) { // Parse .hosts file // Return matched entry fclose(fp); } return NULL; // Fallback to normal resolution }
Compile with: gcc -shared -fPIC -o libcustomhosts.so customhosts.c
Add to ~/.bashrc
: export LD_PRELOAD=/path/to/libcustomhosts.so
Method 2: Bash Alias Wrapper
For simpler cases, create a bash function:
resolve_host() { local host=$1 # Check .hosts first while read -r ip hnames; do if [[ " ${hnames} " == *" ${host} "* ]]; then echo $ip return fi done < ~/.hosts 2>/dev/null # Fallback to normal resolution getent hosts "$host" } alias myping='ping $(resolve_host $1)'
Set up rsync in cron to keep .hosts files consistent across machines:
# In crontab -e */5 * * * * rsync -az user@centralmachine:.hosts ~/.hosts
- Set proper permissions:
chmod 600 ~/.hosts
- Validate IP formats when syncing
- Consider using SSH for rsync transfers
For more complex scenarios:
- Use dnsmasq with its
--hostsdir
option - Implement a simple HTTP DNS server that reads from user-specific files
- Consider Zeroconf/Bonjour for small LANs