1
1
mirror of https://github.com/eblot/pybootd.git synced 2024-08-17 18:00:33 +03:00

More cleanup and removing old useless stuff

This commit is contained in:
Emmanuel Blot 2019-09-03 19:12:16 +02:00
parent d1231d55c0
commit 02c32b412c
6 changed files with 89 additions and 123 deletions

View File

@ -18,33 +18,8 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import os
import sys
def _get_package_name(default='', version='1.6.0'):
try:
from pkg_resources import WorkingSet
except ImportError:
ws = []
else:
ws = WorkingSet()
_path, _ = os.path.split(os.path.dirname(
sys.modules['pybootd'].__file__))
_path = os.path.normpath(_path)
if 'nt' not in os.name:
for dist in ws:
if os.path.samefile(os.path.normpath(dist.location), _path):
return dist.project_name, dist.version
else: # tweak for windows
_path = os.path.abspath(_path).lower()
for dist in ws:
if 'pybootd' in dist.location:
if _path == os.path.abspath(dist.location).lower():
return dist.project_name, dist.version
return default, version
PRODUCT_NAME, __version__ = _get_package_name('pybootd')
__version__ = '1.7.0'
def pybootd_path(path):
@ -61,9 +36,9 @@ def pybootd_path(path):
except ImportError:
raise IOError('pkg_resources module not available')
try:
newpath = resource_filename(Requirement.parse(PRODUCT_NAME), path)
newpath = resource_filename(Requirement.parse('pybootd'), path)
if not newpath:
localpath = get_distribution(PRODUCT_NAME).location
localpath = get_distribution('pybootd').location
newpath = os.path.join(localpath, path)
except DistributionNotFound:
newpath = path

View File

@ -24,7 +24,7 @@
from os.path import isfile
from threading import Thread
from sys import exit as sysexit, modules, stderr
from . import pybootd_path, PRODUCT_NAME, __version__ as VERSION
from . import pybootd_path, __version__
from .pxed import BootpServer
from .tftpd import TftpServer
from .util import logger_factory, EasyConfigParser
@ -94,7 +94,7 @@ def main():
logfile=cfgparser.get('logger', 'file'),
level=cfgparser.get('logger', 'level',
'info'))
logger.info('-'.join((PRODUCT_NAME, VERSION)))
logger.info('-'.join(('pybootd', __version__)))
daemon = None
if not args.tftp:

View File

@ -5,7 +5,8 @@ level = info
[bootp]
address = 0.0.0.0
; pool_start should be in a valid subnet
pool_start = 192.168.25.100
; pool_start = 192.168.25.100
pool_start = 10.113.116.245
pool_count = 5
domain = localdomain
server_name = debug

View File

@ -17,15 +17,15 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import re
import select
import socket
import string
import struct
import sys
import time
from binascii import hexlify
from . import PRODUCT_NAME
from re import compile as recompile
from select import select
from socket import (inet_aton, inet_ntoa, socket,
AF_INET, SOCK_DGRAM, IPPROTO_UDP, SOL_SOCKET,
SO_BROADCAST, SO_REUSEADDR)
from struct import calcsize as scalc, pack as spack, unpack as sunpack
from time import sleep
from traceback import format_exc
from .util import hexline, to_bool, iptoint, inttoip, get_iface_config
BOOTP_PORT_REQUEST = 67
@ -35,9 +35,9 @@ BOOTREQUEST = 1
BOOTREPLY = 2
BOOTPFORMAT = '!4bIHH4s4s4s4s16s64s128s64s'
BOOTPFORMATSIZE = struct.calcsize(BOOTPFORMAT)
BOOTPFORMATSIZE = scalc(BOOTPFORMAT)
DHCPFORMAT = '!4bIHH4s4s4s4s16s64s128s4s'
DHCPFORMATSIZE = struct.calcsize(DHCPFORMAT)
DHCPFORMATSIZE = scalc(DHCPFORMAT)
(BOOTP_OP, BOOTP_HTYPE, BOOTP_HLEN, BOOTP_HOPS, BOOTP_XID, BOOTP_SECS,
BOOTP_FLAGS, BOOTP_CIADDR, BOOTP_YIADDR, BOOTP_SIADDR, BOOTP_GIADDR,
@ -177,9 +177,7 @@ class BootpServer:
self.ippool = {} # key MAC address string, value assigned IP string
self.filepool = {} # key IP string, value pathname
self.states = {} # key MAC address string, value client state
name_ = PRODUCT_NAME.split('-')
name_[0] = 'bootp'
self.bootp_section = '_'.join(name_)
self.bootp_section = 'bootp'
self.pool_start = self.config.get(self.bootp_section, 'pool_start')
if not self.pool_start:
raise BootpError('Missing pool_start definition')
@ -236,7 +234,7 @@ class BootpServer:
msg = ','.join([notice, uuid_str, mac_str, ip])
else:
msg = ','.join([notice, mac_str, ip])
notify_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
notify_sock = socket(AF_INET, SOCK_DGRAM)
for n in self.notify:
self.log.info('Notifying %s with %s' % (n, msg))
notify_sock.sendto(msg, n)
@ -249,10 +247,9 @@ class BootpServer:
host = self.config.get(self.bootp_section, 'address', '0.0.0.0')
port = self.config.get(self.bootp_section, 'port',
str(BOOTP_PORT_REQUEST))
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM,
socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
sock.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
self.sock.append(sock)
self.log.info('Listening to %s:%s' % (host, port))
sock.bind((host, int(port)))
@ -260,14 +257,13 @@ class BootpServer:
def forever(self):
while True:
try:
r, w, e = select.select(self.sock, [], self.sock)
r, w, e = select(self.sock, [], self.sock)
for sock in r:
data, addr = sock.recvfrom(556)
self.handle(sock, addr, data)
except Exception as exc:
import traceback
self.log.critical('%s\n%s' % (exc, traceback.format_exc()))
time.sleep(1)
self.log.critical('%s\n%s' % (exc, format_exc()))
sleep(1)
def parse_options(self, tail):
self.log.debug('Parsing DHCP options')
@ -280,7 +276,7 @@ class BootpServer:
if tag == 0xff:
return dhcp_tags
length = ord(tail[1])
(value, ) = struct.unpack('!%ss' % length, tail[2:2+length])
(value, ) = sunpack('!%ss' % length, tail[2:2+length])
tail = tail[2+length:]
try:
option = DHCP_OPTIONS[tag]
@ -296,25 +292,25 @@ class BootpServer:
try:
buf = ''
uuid = options[97]
buf += struct.pack('!BB%ds' % len(uuid),
buf += spack('!BB%ds' % len(uuid),
97, len(uuid), uuid)
clientclass = options[60]
clientclass = clientclass[:clientclass.find(':')]
buf += struct.pack('!BB%ds' % len(clientclass),
buf += spack('!BB%ds' % len(clientclass),
60, len(clientclass), clientclass)
vendor = ''
vendor += struct.pack('!BBB', PXE_DISCOVERY_CONTROL, 1, 0x0A)
vendor += struct.pack('!BBHB4s', PXE_BOOT_SERVERS, 2+1+4,
vendor += spack('!BBB', PXE_DISCOVERY_CONTROL, 1, 0x0A)
vendor += spack('!BBHB4s', PXE_BOOT_SERVERS, 2+1+4,
0, 1, server)
srvstr = 'Python'
vendor += struct.pack('!BBHB%ds' % len(srvstr), PXE_BOOT_MENU,
vendor += spack('!BBHB%ds' % len(srvstr), PXE_BOOT_MENU,
2+1+len(srvstr), 0, len(srvstr), srvstr)
prompt = 'Stupid PXE'
vendor += struct.pack('!BBB%ds' % len(prompt), PXE_MENU_PROMPT,
vendor += spack('!BBB%ds' % len(prompt), PXE_MENU_PROMPT,
1+len(prompt), len(prompt), prompt)
buf += struct.pack('!BB%ds' % len(vendor), 43,
buf += spack('!BB%ds' % len(vendor), 43,
len(vendor), vendor)
buf += struct.pack('!BBB', 255, 0, 0)
buf += spack('!BBB', 255, 0, 0)
return buf
except KeyError as exc:
self.log.error('Missing options, cancelling: %s' % exc)
@ -324,7 +320,7 @@ class BootpServer:
buf = ''
if not clientname:
return buf
buf += struct.pack('!BB%ds' % len(clientname),
buf += spack('!BB%ds' % len(clientname),
12, len(clientname), clientname)
return buf
@ -333,7 +329,7 @@ class BootpServer:
if len(data) < DHCPFORMATSIZE:
self.log.error('Cannot be a DHCP or BOOTP request - too small!')
tail = data[DHCPFORMATSIZE:]
buf = list(struct.unpack(DHCPFORMAT, data[:DHCPFORMATSIZE]))
buf = list(sunpack(DHCPFORMAT, data[:DHCPFORMATSIZE]))
if buf[BOOTP_OP] != BOOTREQUEST:
self.log.warn('Not a BOOTREQUEST')
return
@ -477,7 +473,7 @@ class BootpServer:
# construct reply
buf[BOOTP_HOPS] = 0
buf[BOOTP_OP] = BOOTREPLY
self.log.info('Client IP: %s' % socket.inet_ntoa(buf[7]))
self.log.info('Client IP: %s' % inet_ntoa(buf[7]))
if buf[BOOTP_CIADDR] == '\x00\x00\x00\x00':
self.log.debug('Client needs its address')
ipaddr = iptoint(self.pool_start)
@ -501,20 +497,20 @@ class BootpServer:
self.bootp_section, 'netmask', self.netconfig['mask']))
reply_broadcast = iptoint(ip) & mask
reply_broadcast |= (~mask) & ((1 << 32)-1)
buf[BOOTP_YIADDR] = socket.inet_aton(ip)
buf[BOOTP_YIADDR] = inet_aton(ip)
buf[BOOTP_SECS] = 0
buf[BOOTP_FLAGS] = BOOTP_FLAGS_BROADCAST
relay = buf[BOOTP_GIADDR]
if relay != b'\x00\x00\x00\x00':
addr = (socket.inet_ntoa(relay), addr[1])
addr = (inet_ntoa(relay), addr[1])
else:
addr = (inttoip(reply_broadcast), addr[1])
self.log.info('Reply to: %s:%s' % addr)
else:
buf[BOOTP_YIADDR] = buf[BOOTP_CIADDR]
ip = socket.inet_ntoa(buf[BOOTP_YIADDR])
buf[BOOTP_SIADDR] = socket.inet_aton(server_addr)
ip = inet_ntoa(buf[BOOTP_YIADDR])
buf[BOOTP_SIADDR] = inet_aton(server_addr)
# sname
buf[BOOTP_SNAME] = \
'.'.join([self.config.get(self.bootp_section,
@ -567,38 +563,38 @@ class BootpServer:
else:
self.log.debug('No filename defined for IP %s' % ip)
pkt = struct.pack(DHCPFORMAT, *buf)
pkt += struct.pack('!BBB', DHCP_MSG, 1, dhcp_reply)
server = socket.inet_aton(server_addr)
pkt += struct.pack('!BB4s', DHCP_SERVER, 4, server)
pkt = spack(DHCPFORMAT, *buf)
pkt += spack('!BBB', DHCP_MSG, 1, dhcp_reply)
server = inet_aton(server_addr)
pkt += spack('!BB4s', DHCP_SERVER, 4, server)
mask = socket.inet_aton(self.config.get(
mask = inet_aton(self.config.get(
self.bootp_section, 'netmask', self.netconfig['mask']))
pkt += struct.pack('!BB4s', DHCP_IP_MASK, 4, mask)
pkt += spack('!BB4s', DHCP_IP_MASK, 4, mask)
gateway_addr = self.config.get(self.bootp_section, 'gateway', '')
if gateway_addr:
gateway = socket.inet_aton(gateway_addr)
gateway = inet_aton(gateway_addr)
else:
gateway = server
pkt += struct.pack('!BB4s', DHCP_IP_GATEWAY, 4, gateway)
pkt += spack('!BB4s', DHCP_IP_GATEWAY, 4, gateway)
dns = self.config.get(self.bootp_section,
'dns', None)
if dns:
if dns.lower() == 'auto':
dns_list = self.get_dns_servers() or [socket.inet_ntoa(server)]
dns_list = self.get_dns_servers() or [inet_ntoa(server)]
else:
dns_list = dns.split(';')
for dns_str in dns_list:
dns_ip = socket.inet_aton(dns_str)
pkt += struct.pack('!BB4s', DHCP_IP_DNS, 4, dns_ip)
pkt += struct.pack('!BBI', DHCP_LEASE_TIME, 4,
dns_ip = inet_aton(dns_str)
pkt += spack('!BB4s', DHCP_IP_DNS, 4, dns_ip)
pkt += spack('!BBI', DHCP_LEASE_TIME, 4,
int(self.config.get(self.bootp_section,
'lease_time',
str(24*3600))))
pkt += struct.pack('!BB', DHCP_END, 0)
pkt += spack('!BB', DHCP_END, 0)
# do not attempt to produce a PXE-augmented response for
# regular DHCP requests
@ -623,7 +619,7 @@ class BootpServer:
self.states[mac_str] = newstate
def get_dns_servers(self):
nscre = re.compile(r'nameserver\s+(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})\s')
nscre = recompile(r'nameserver\s+(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})\s')
result = []
try:
with open('/etc/resolv.conf', 'r') as resolv:

View File

@ -18,22 +18,21 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import os
import re
import select
import socket
import string
import struct
import sys
import time
from configparser import NoSectionError
from io import StringIO
from re import compile as recompile, sub as resub
from select import select
from socket import socket, AF_INET, SOCK_DGRAM
from struct import pack as spack, unpack as sunpack
from sys import argv, exc_info
from threading import Thread
from time import time as now
from traceback import print_exc
from urllib.parse import urlparse
from urllib.request import urlopen
from . import pybootd_path
from .util import hexline
__all__ = ['TftpServer']
TFTP_PORT = 69
@ -86,7 +85,7 @@ class TftpConnection(object):
timeout = self.timeout
retry = self.server.retry
while retry:
r, w, e = select.select([fno], [], [fno], timeout)
r, w, e = select([fno], [], [fno], timeout)
if not r:
# We timed out -- retransmit
retry = retry - 1
@ -121,13 +120,13 @@ class TftpConnection(object):
path = self.server.bootpd.get_filename(client_ip)
return path
def parse(self, data, unpack=struct.unpack):
def parse(self, data, unpack=sunpack):
self.log.debug('parse')
buf = buffer(data)
pkt = {}
opcode = pkt['opcode'] = unpack('!h', buf[:2])[0]
if (opcode == self.RRQ) or (opcode == self.WRQ):
resource, mode, options = string.split(data[2:], '\000', 2)
resource, mode, options = data[2:].split('\000', 2)
resource = self.server.fcre.sub(self._filter_file, resource)
if self.server.root and self.is_url(self.server.root):
resource = '%s/%s' % (self.server.root, resource)
@ -138,7 +137,7 @@ class TftpConnection(object):
if not self.server.genfilecre.match(resource):
if resource.startswith('^%s' % os.sep):
resource = os.path.join(
os.path.dirname(sys.argv[0]),
os.path.dirname(argv[0]),
resource.lstrip('^%s' % os.sep))
elif self.server.root:
if self.server.root.startswith(os.sep):
@ -147,7 +146,7 @@ class TftpConnection(object):
resource)
else:
# Relative root directory, from the daemon path
daemonpath = os.path.dirname(sys.argv[0])
daemonpath = os.path.dirname(argv[0])
if not daemonpath.startswith(os.sep):
daemonpath = os.path.normpath(
os.path.join(os.getcwd(), daemonpath))
@ -220,8 +219,7 @@ class TftpConnection(object):
except TftpError as exc:
self.send_error(exc[0], exc[1])
except:
import traceback
self.log.error(traceback.format_exc())
self.log.error(format_exc())
self.log.debug('Ending connection %s:%s' % addr)
def recv_ack(self, pkt):
@ -245,19 +243,19 @@ class TftpConnection(object):
self.handle_err(pkt)
self.retransmit()
def send_data(self, data, pack=struct.pack):
def send_data(self, data, pack=spack):
self.log.debug('send_data')
if not self.time:
self.time = time.time()
self.time = now()
blocksize = self.blocksize
block = self.blockNumber = self.blockNumber + 1
lendata = len(data)
format = '!hh%ds' % lendata
pkt = pack(format, self.DATA, block, data)
fmt = '!hh%ds' % lendata
pkt = pack(fmt, self.DATA, block, data)
self.send(pkt)
self.active = (len(data) == blocksize)
if not self.active and self.time:
total = time.time()-self.time
total = now()-self.time
self.time = 0
try:
name = self.file.name
@ -271,26 +269,24 @@ class TftpConnection(object):
# StringIO does not have a 'name' attribute
pass
except Exception:
import traceback
traceback.print_exc()
pass
print_exc()
def send_ack(self, pack=struct.pack):
def send_ack(self, pack=spack):
self.log.debug('send_ack')
block = self.blockNumber
self.blockNumber = self.blockNumber + 1
format = '!hh'
pkt = pack(format, self.ACK, block)
fmt = '!hh'
pkt = pack(fmt, self.ACK, block)
self.send(pkt)
def send_error(self, errnum, errtext, pack=struct.pack):
def send_error(self, errnum, errtext, pack=spack):
self.log.debug('send_error')
errtext = errtext + '\000'
format = '!hh%ds' % len(errtext)
outdata = pack(format, self.ERR, errnum, errtext)
fmt = '!hh%ds' % len(errtext)
outdata = pack(fmt, self.ERR, errnum, errtext)
self.sock.sendto(outdata, self.client_addr)
def send_oack(self, options, pack=struct.pack):
def send_oack(self, options, pack=spack):
self.log.debug('send_oack')
pkt = pack('!h', self.OACK)
for k, v in options:
@ -341,7 +337,7 @@ class TftpConnection(object):
except Exception:
self.send_error(1, 'Cannot open resource')
self.log.warn('Cannot open file for reading %s: %s' %
sys.exc_info()[:2])
exc_info()[:2])
return
if 'tsize' not in pkt:
self.send_data(self.file.read(self.blocksize))
@ -359,7 +355,7 @@ class TftpConnection(object):
except:
self.send_error(1, 'Cannot open file')
self.log.error('Cannot open file for writing %s: %s' %
sys.exc_info()[:2])
exc_info()[:2])
return
self.send_ack()
@ -398,7 +394,7 @@ class TftpServer:
self.retry = int(self.config.get('tftp', 'blocksize', '5'))
self.root = self.config.get('tftp', 'root', os.getcwd())
self.fcre, self.filepatterns = self.get_file_filters()
self.genfilecre = re.compile(r'\[(?P<name>[\w\.\-]+)\]')
self.genfilecre = recompile(r'\[(?P<name>[\w\.\-]+)\]')
def bind(self):
netconfig = self.bootpd and self.bootpd.get_netconfig()
@ -407,7 +403,7 @@ class TftpServer:
if not host:
raise TftpError('TFTP address no defined')
port = int(self.config.get('tftp', 'port', str(TFTP_PORT)))
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock = socket(AF_INET, SOCK_DGRAM)
self.sock.append(sock)
sock.bind((host, port))
@ -417,7 +413,7 @@ class TftpServer:
if not self.bootpd.is_alive():
self.log.info('Bootp daemon is dead, exiting')
break
r, w, e = select.select(self.sock, [], self.sock)
r, w, e = select(self.sock, [], self.sock)
for sock in r:
data, addr = sock.recvfrom(516)
tc = TftpConnection(self)
@ -433,7 +429,7 @@ class TftpServer:
if not filename:
continue
filepattern = self.filepatterns[group]
return re.sub(r'\{(\w+)\}', connexion._dynreplace, filepattern)
return resub(r'\{(\w+)\}', connexion._dynreplace, filepattern)
raise TftpError('Internal error, file matching pattern issue')
def get_file_filters(self):
@ -451,4 +447,4 @@ class TftpServer:
xre = r'^(?:\./)?(?:%s)$' % r'|'.join(patterns)
except NoSectionError:
xre = r'^$'
return (re.compile(xre), replacements)
return (recompile(xre), replacements)

View File

@ -17,7 +17,6 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from array import array
from configparser import SafeConfigParser, InterpolationSyntaxError
from logging import (DEBUG, INFO, ERROR, CRITICAL, WARNING,
Formatter, FileHandler, StreamHandler, getLogger)
@ -29,7 +28,6 @@ from subprocess import run
from struct import pack as spack, unpack as sunpack
from sys import stderr
try:
import netifaces
except ImportError:
@ -112,7 +110,7 @@ def hexline(data, sep=' '):
of the buffer data
"""
try:
if isinstance(data, (bytes, array)):
if isinstance(data, bytes):
src = bytearray(data)
elif isinstance(data, bytearray):
src = data