penguins-eggs
Version:
A remaster system tool, compatible with Almalinux, Alpine, Arch, Debian, Devuan, Fedora, Manjaro, Opensuse, Ubuntu and derivatives
117 lines (116 loc) • 5.46 kB
JavaScript
/**
* simple-proxy.ts
* Un proxy DHCP minimale che agisce come un mini-server DHCP completo
* solo per i client PXE, per aggirare le protezioni di rete.
*/
import ansis from 'ansis';
import { createSocket } from 'dgram';
import { DhcpMessageType } from '../dhcpd-proxy//lib/packet/message-types.js';
import { Packet } from '../dhcpd-proxy/classes/packet.js';
// Un semplice Map in memoria per tracciare gli IP offerti
const offeredIPs = new Map();
/**
* La nostra unica funzione. Prende le opzioni e avvia il proxy.
* @param options Le opzioni di configurazione PXE
*/
export function startSimpleProxy(options) {
const socket = createSocket({ reuseAddr: true, type: 'udp4' });
socket.on('error', (err) => {
console.error(ansis.red.bold(`ERRORE SOCKET: ${err.stack}`));
socket.close();
});
socket.on('listening', () => {
const address = socket.address();
socket.setBroadcast(true);
console.log(ansis.green.inverse(`\n Proxy DHCP listening su ${address.address}:${address.port}. CTRL-C to end!\n`));
});
socket.on('message', (buffer, rinfo) => {
try {
const packet = Packet.fromBuffer(buffer);
packet.remote = rinfo;
const messageType = packet.options[53] || 0;
if (packet.op === 1 && messageType === DhcpMessageType.DHCPDISCOVER) {
handleDiscover(packet, options);
}
else if (packet.op === 1 && messageType === DhcpMessageType.DHCPREQUEST) {
handleRequest(packet, options);
}
}
catch {
// Ignoriamo errori di parsing
}
});
socket.bind(67, '0.0.0.0');
// --- Funzioni Helper Interne ---
function setBootFilename(packet, opts) {
const isIpxe = packet.options[77] && packet.options[77].toString().includes('iPXE');
const arch = packet.options[93] || packet.options[60];
const isUefi64 = arch && (String(arch).includes('00007') || String(arch).includes('00009'));
// LOGICA DI ASSEGNAZIONE PRECISA
if (isIpxe && isUefi64) {
// CASO 1: Il client è iPXE e anche UEFI. Offriamo lo script!
console.log(ansis.magenta.bold(`<- Rilevato client iPXE su UEFI! Offro script autoexec...`));
return `http://${opts.host}/autoexec.ipxe`;
}
if (isUefi64) {
// CASO 2: Il client è UEFI ma non è ancora iPXE. Offriamo il bootloader iPXE per UEFI.
return opts.efi64_filename;
}
// CASO 3: Il client è BIOS. Offriamo il bootloader iPXE per BIOS.
// (Aggiunta logica per altri tipi di architettura se necessario)
return opts.bios_filename;
}
function handleDiscover(packet, opts) {
console.log(ansis.blue.bold(`\n-> DISCOVER Ricevuto`));
console.log(ansis.dim(` └─ Da MAC: ${ansis.white(packet.chaddr)}`));
if (packet.options[60]?.includes('PXEClient')) {
console.log(ansis.cyan.bold(`<- Rilevato PXEClient! Preparo un'offerta completa...`));
const offerPacket = new Packet(packet);
offerPacket.op = 2; // BOOTREPLY
const ipParts = opts.host.split('.');
ipParts[3] = (Number.parseInt(ipParts[3]) + 10).toString();
const offeredIp = ipParts.join('.');
offeredIPs.set(packet.chaddr, offeredIp);
offerPacket.yiaddr = offeredIp;
offerPacket.siaddr = opts.tftpserver;
offerPacket.options[53] = DhcpMessageType.DHCPOFFER;
offerPacket.options[54] = opts.host;
// Imposta il filename usando la nuova funzione logica
offerPacket.fname = setBootFilename(packet, opts);
console.log(ansis.dim(` ├─ Offro IP: ${ansis.green(offeredIp)}`));
console.log(ansis.dim(` ├─ Next-Server: ${ansis.white(offerPacket.siaddr)}`));
console.log(ansis.dim(` └─ Filename: ${ansis.white(offerPacket.fname)}`));
send(offerPacket, opts.broadcast, 68);
}
}
function handleRequest(packet, opts) {
const offeredIp = offeredIPs.get(packet.chaddr);
if (!offeredIp || (packet.options[50] && packet.options[50] !== offeredIp)) {
return;
}
console.log(ansis.yellow.bold(`-> REQUEST per la nostra offerta ricevuto!`));
console.log(ansis.dim(` └─ Da MAC: ${ansis.white(packet.chaddr)}`));
const ackPacket = new Packet(packet);
ackPacket.op = 2;
ackPacket.yiaddr = offeredIp;
ackPacket.siaddr = opts.tftpserver;
ackPacket.options[53] = DhcpMessageType.DHCPACK;
ackPacket.options[54] = opts.host;
// Imposta il filename usando la stessa logica
ackPacket.fname = setBootFilename(packet, opts);
console.log(ansis.cyan.bold(`<- Invio ACK finale...`));
send(ackPacket, opts.broadcast, 68);
}
function send(packet, ip, port) {
const buffer = packet.toBuffer();
const typeName = DhcpMessageType[packet.options[53]] || 'UNKNOWN';
socket.send(buffer, 0, buffer.length, port, ip, (err, bytes) => {
if (err) {
console.error(ansis.red.bold(`[PROXY-ERROR] Errore invio ${typeName}:`), err);
}
else {
console.log(ansis.green(` ✔︎ ${typeName} inviata a ${ip}:${port} (${bytes} bytes)`));
}
});
}
}