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
--hostsdiroption - Implement a simple HTTP DNS server that reads from user-specific files
- Consider Zeroconf/Bonjour for small LANs