Initial checkin of a DHCP server (untested)
authorQuentin Smith <quentin@mit.edu>
Thu, 11 Oct 2007 07:56:09 +0000 (03:56 -0400)
committerQuentin Smith <quentin@mit.edu>
Thu, 11 Oct 2007 07:56:09 +0000 (03:56 -0400)
svn path=/trunk/dhcp/; revision=189

33 files changed:
anemon-svn.tar.gz [new file with mode: 0644]
dhcpserver.py [new file with mode: 0644]
event_logger.py [new file with mode: 0644]
pydhcplib/COPYING [new file with mode: 0644]
pydhcplib/README [new file with mode: 0644]
pydhcplib/examples/client_example.py [new file with mode: 0755]
pydhcplib/examples/gen_packet_example.py [new file with mode: 0755]
pydhcplib/examples/hwaddr_example.py [new file with mode: 0644]
pydhcplib/examples/ipv4_example.py [new file with mode: 0755]
pydhcplib/examples/server_example.py [new file with mode: 0644]
pydhcplib/examples/strlist_example.py [new file with mode: 0755]
pydhcplib/man/fr/man3/pydhcplib.3.gz [new file with mode: 0644]
pydhcplib/man/fr/man3/pydhcplib.DhcpBasicPacket.3.gz [new file with mode: 0644]
pydhcplib/man/fr/man3/pydhcplib.DhcpPacket.3.gz [new file with mode: 0644]
pydhcplib/man/fr/man3/pydhcplib.hwmac.3.gz [new file with mode: 0644]
pydhcplib/man/fr/man3/pydhcplib.ipv4.3.gz [new file with mode: 0644]
pydhcplib/man/fr/man3/pydhcplib.strlist.3.gz [new file with mode: 0644]
pydhcplib/man/fr/man8/pydhcp.8.gz [new file with mode: 0644]
pydhcplib/man/man3/pydhcplib.3.gz [new file with mode: 0644]
pydhcplib/man/man3/pydhcplib.ipv4.3.gz [new file with mode: 0644]
pydhcplib/man/man3/pydhcplib.strlist.3.gz [new file with mode: 0644]
pydhcplib/man/man8/pydhcp.8.gz [new file with mode: 0644]
pydhcplib/pydhcplib/__init__.py [new file with mode: 0644]
pydhcplib/pydhcplib/dhcp_basic_packet.py [new file with mode: 0644]
pydhcplib/pydhcplib/dhcp_constants.py [new file with mode: 0644]
pydhcplib/pydhcplib/dhcp_network.py [new file with mode: 0644]
pydhcplib/pydhcplib/dhcp_packet.py [new file with mode: 0644]
pydhcplib/pydhcplib/type_hw_addr.py [new file with mode: 0644]
pydhcplib/pydhcplib/type_ipv4.py [new file with mode: 0644]
pydhcplib/pydhcplib/type_strlist.py [new file with mode: 0644]
pydhcplib/scripts/pydhcp [new file with mode: 0644]
pydhcplib/scripts/pydhcp.py [new file with mode: 0644]
pydhcplib/setup.py [new file with mode: 0755]

diff --git a/anemon-svn.tar.gz b/anemon-svn.tar.gz
new file mode 100644 (file)
index 0000000..73466c8
Binary files /dev/null and b/anemon-svn.tar.gz differ
diff --git a/dhcpserver.py b/dhcpserver.py
new file mode 100644 (file)
index 0000000..5c7721c
--- /dev/null
@@ -0,0 +1,159 @@
+#!/usr/bin/python
+import pydhcplib
+from pydhcplib.dhcp_packet import *
+from pydhcplib.type_hw_addr import hwmac
+from pydhcplib.type_ipv4 import ipv4
+
+from event_logger import Log
+
+import sipb_xen_database
+
+class DhcpBackend:
+    def __init__(self, database=None):
+        if database is not None:
+            sipb_xen_database.connect(database)
+    def findIP(self, mac):
+        value = sipb_xen_database.NIC.get_by(mac_addr=mac)
+        if value is None:
+            return None
+        ip = value.ip
+        if ip is None:  #Deactivated?
+            return None
+        return ip
+
+    def Discover(self, packet):
+        Log.Output(Log.debug,"dhcp_backend : Discover ")
+        chaddr = hwmac(packet.GetHardwareAddress())
+        ip = self.findIP(str(chaddr))
+        if ip is not None:
+            ip = ipv4(ip)
+            Log.Output(Log.debug,"dhcp_backend : Discover result = "+str(ip))
+            packet_parameters = {}
+
+            # FIXME: Other offer parameters go here
+            packet_parameters["yiaddr"] = ip.list()
+            
+            packet.SetMultipleOptions(packet_parameters)
+            return True
+        return False
+        
+    def Request(self, packet):
+        Log.Output(Log.debug, "dhcp_backend : Request")
+        
+        discover = self.Discover(packet)
+        
+        chaddr = hwmac(packet.GetHardwareAddress())
+        request = packet.GetOption("request_ip_address")
+        yiaddr = packet.GetOption("yiaddr")
+
+        if not discover:
+            Log.Output(Log.info,"Unknown MAC address: "+str(chaddr))
+            return False
+        
+        if yiaddr!="0.0.0.0" and yiaddr == request :
+            Log.Output(Log.info,"Ack ip "+str(yiaddr)+" for "+str(chaddr))
+            return True
+        else:
+            Log.Output(Log.info,"Requested ip "+str(request)+" not available for "+str(chaddr))
+        return False
+
+    def Decline(self, packet):
+        pass
+    def Release(self, packet):
+        pass
+    
+
+class DhcpServer(pydhcplib.dhcp_network.DhcpServer):
+    def __init__(self, backend, options = {'client_listenport':68,'server_listenport':67}):
+        pydhcplib.dhcp_network.DhcpServer.__init__(self,"0.0.0.0",options["client_listen_port"],options["server_listen_port"],)
+        self.backend = backend
+        Log.Output(Log.debug, "__init__ DhcpServer")
+
+    def SendPacket(self, packet):
+            """Encode and send the packet."""
+        
+        giaddr = packet.GetOption('giaddr')
+
+        # in all case, if giaddr is set, send packet to relay_agent
+        # network address defines by giaddr
+        if giaddr!=[0,0,0,0] :
+            agent_ip = ".".join(map(str,giaddr))
+            self.SendDhcpPacketTo(agent_ip,packet)
+            Log.Output(Log.debug, "SendPacket to agent : "+agent_ip)
+
+        # FIXME: This shouldn't broadcast if it has an IP address to send
+        # it to instead. See RFC2131 part 4.1 for full details
+        else :
+            Log.Output(Log.debug, "No agent, broadcast packet.")
+            self.SendDhcpPacketTo("255.255.255.255",packet)
+            
+
+    def HandleDhcpDiscover(self, packet):
+        """Build and send DHCPOFFER packet in response to DHCPDISCOVER
+        packet."""
+
+        logmsg = "Get DHCPDISCOVER packet from " + hwmac(packet.GetHardwareAddress()).str()
+
+        Log.Output(Log.info, logmsg)
+        offer = DhcpPacket()
+        offer.CreateDhcpOfferPacketFrom(packet)
+        
+        if self.backend.Discover(offer) :
+            self.SendPacket(offer)
+        # FIXME : what if false ?
+
+
+    def HandleDhcpRequest(self, packet):
+        """Build and send DHCPACK or DHCPNACK packet in response to
+        DHCPREQUEST packet. 4 types of DHCPREQUEST exists."""
+
+        ip = packet.GetOption("request_ip_address")
+        sid = packet.GetOption("server_identifier")
+        ciaddr = packet.GetOption("ciaddr")
+
+        if sid != [0,0,0,0] and ciaddr == [0,0,0,0] :
+            Log.Output(Log.info, "Get DHCPREQUEST_SELECTING_STATE packet")
+
+        elif sid == [0,0,0,0] and ciaddr == [0,0,0,0] and ip :
+            Log.Output(Log.info, "Get DHCPREQUEST_INITREBOOT_STATE packet")
+
+        elif sid == [0,0,0,0] and ciaddr != [0,0,0,0] and not ip :
+            Log.Output(Log.info,"Get DHCPREQUEST_INITREBOOT_STATE packet")
+
+        else : Log.Output(Log.info,"Get DHCPREQUEST_UNKNOWN_STATE packet : not implemented")
+
+        if self.backend.Request(packet) : packet.TransformToDhcpAckPacket()
+        else : packet.TransformToDhcpNackPacket()
+
+        self.SendPacket(packet)
+
+
+
+    # FIXME: These are not yet implemented.
+    def HandleDhcpDecline(self, packet):
+        Log.Output(Log.info, "Get DHCPDECLINE packet")
+        self.backend.Decline(packet)
+        
+    def HandleDhcpRelease(self, packet):
+        Log.Output(Log.info,"Get DHCPRELEASE packet")
+        self.backend.Release(packet)
+        
+    def HandleDhcpInform(self, packet):
+        Log.Output(Log.info, "Get DHCPINFORM packet")
+
+        if self.backend.Request(packet) :
+            packet.TransformToDhcpAckPacket()
+            # FIXME : Remove lease_time from options
+            self.SendPacket(packet)
+
+        # FIXME : what if false ?
+
+if '__main__' == __name__:
+    event_logger.init("stdout", event_logger.INFO, {})
+    options = { "server_listen_port":67,
+                "client_listen_port":68,
+                "listen_address":"0.0.0.0"}
+    backend = DhcpBackend('postgres://sipb-xen@sipb-xen-dev/sipb_xen')
+    server = DhcpServer(backend, options)
+
+    while True : server.GetNextDhcpPacket()
diff --git a/event_logger.py b/event_logger.py
new file mode 100644 (file)
index 0000000..fa59be0
--- /dev/null
@@ -0,0 +1,63 @@
+# Anemon Dhcp
+# Copyright (C) 2005 Mathieu Ignacio -- mignacio@april.org
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+import sys
+from logging import *
+
+
+
+class EventLogger:
+    def __init__(self,logtype="stdout",loglevel=WARNING,option={}):
+        self.loglevel = loglevel
+        self.logtype = logtype
+
+        self.info = INFO
+        self.debug = DEBUG
+        self.warn = WARN
+        self.error = ERROR
+        self.critical = CRITICAL
+
+
+        self.logger = getLogger('SipbXenDhcpServer')
+
+        if logtype == "file" :
+            # into file logger
+            handler = FileHandler(option["log_file"])
+            
+        elif logtype == "syslog" :
+            handler = SysLogHandler((option["log_host"],option["log_port"]))
+
+        elif logtype == "http" :
+            handler = HTTPHandler(option["log_host"],option["log_url"],option["log_method"])
+
+        else : # logtype == "stdout" :
+            handler = StreamHandler()
+
+
+
+        handler.setFormatter(Formatter('%(asctime)s %(levelname)s %(message)s'))
+        self.logger.addHandler(handler) 
+        self.logger.setLevel(loglevel)
+
+    def Output(self,level,infostring) :
+        self.logger.log(level,infostring)
+
+def init(logtype,level,path):
+    global Log
+    
+    Log = EventLogger(logtype,eval(level),path)
+    Log.Output(INFO,"EventLogger : Started.")
diff --git a/pydhcplib/COPYING b/pydhcplib/COPYING
new file mode 100644 (file)
index 0000000..4a4f44a
--- /dev/null
@@ -0,0 +1,280 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
diff --git a/pydhcplib/README b/pydhcplib/README
new file mode 100644 (file)
index 0000000..04eadbb
--- /dev/null
@@ -0,0 +1,20 @@
+Pydhcplib is a python library to read/write and encode/decode dhcp
+packet on network. 
+
+
+
+Installation :
+--------------
+On Debian Sarge, simply run "./setup.py install". Python modules will be
+installed in /usr/lib/python2.X/site-packages/pydhcplib/. 
+
+If you want to install it on a different location, use the --prefix on 
+the setup.py command line like this : 
+./setup.py install --prefix=/rootpath/to/your/location/
+
+
+How to use pydhcplib :
+----------------------
+Look in the examples directory to learn how to use the modules.
+man pydhcp
+man pydhcplib
diff --git a/pydhcplib/examples/client_example.py b/pydhcplib/examples/client_example.py
new file mode 100755 (executable)
index 0000000..c8f3887
--- /dev/null
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+#
+# pydhcplib
+# Copyright (C) 2005,2006 Mathieu Ignacio -- mignacio@april.org
+#
+# This file is part of pydhcplib.
+# Pydhcplib is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+from pydhcplib.dhcp_packet import *
+from pydhcplib.dhcp_network import *
+
+
+netopt = {'client_listen_port':68,
+           'server_listen_port':67,
+           'listen_address':"0.0.0.0"}
+
+class Client(DhcpClient):
+    def __init__(self, options):
+        DhcpClient.__init__(self,options["listen_address"],
+                            options["client_listen_port"],
+                            options["server_listen_port"])
+        
+    def HandleDhcpOffer(self, packet):
+        packet.PrintHeaders()
+        packet.PrintOptions()
+        
+    def HandleDhcpAck(self, packet):
+        packet.PrintHeaders()
+        packet.PrintOptions()
+
+    def HandleDhcpNack(self, packet):
+        packet.PrintHeaders()
+        packet.PrintOptions()
+        
+
+client = Client(netopt)
+
+while True :
+    client.GetNextDhcpPacket()
diff --git a/pydhcplib/examples/gen_packet_example.py b/pydhcplib/examples/gen_packet_example.py
new file mode 100755 (executable)
index 0000000..c6e7550
--- /dev/null
@@ -0,0 +1,16 @@
+#!/usr/bin/python
+
+from pydhcplib.dhcp_packet import DhcpPacket
+from pydhcplib.type_strlist import strlist
+from pydhcplib.type_ipv4 import ipv4
+
+
+packet = DhcpPacket()
+
+packet.SetOption("domain_name",strlist("anemon.org").list())
+packet.SetOption("router",ipv4("192.168.0.1").list()+[6,4,2,1])
+packet.SetOption("time_server",[100,100,100,7,6,4,2,1])
+packet.SetOption("yiaddr",[192,168,0,18])
+
+packet.PrintHeaders()
+packet.PrintOptions()
diff --git a/pydhcplib/examples/hwaddr_example.py b/pydhcplib/examples/hwaddr_example.py
new file mode 100644 (file)
index 0000000..9c5de80
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/python
+
+from pydhcplib.type_hw_addr import hwmac
+
+
+address = hwmac()
+print "a0 : ",address
+
+address1 = hwmac("ff:11:22:33:44:55")
+print "a1 : ",address1
+
+address2 = hwmac("f6.16.26.36.46.56")
+print "a2 : ",address2
+
+address3 = hwmac("ff.11-22:33-44.55")
+print "a3 : ",address3
+
+
+
+if address1 == address2 : print "test 1 : ",address1, "==",address2
+else : print "test 1 : " ,address1, "!=",address2
+
+if address1 == address3 : print "test 2 : ", address1, "==",address3
+else : print "test 2 : ", address1, "!=",address3
+
+
+
+address4 = hwmac([186, 45, 67, 176, 6, 11])
+address5 = hwmac("ba:2d:43:b0:06:0c")
+    
+print "b0 : ", address4,address4.list()
+print "b1 : ", address5,address5.list()
diff --git a/pydhcplib/examples/ipv4_example.py b/pydhcplib/examples/ipv4_example.py
new file mode 100755 (executable)
index 0000000..2a74c67
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/python
+
+
+from pydhcplib.type_ipv4 import ipv4
+
+
+address = ipv4()
+print "a0 : ",address
+
+address1 = ipv4("192.168.0.1")
+print "a1 : ",address1
+
+address2 = ipv4("10.0.0.1")
+print "a2 : ",address2
+
+address3 = ipv4([192,168,0,1])
+print "a3 : ",address3
+
+
+
+if address1 == address2 : print "test 1 : ",address1, "==",address2
+else : print "test 1 : " ,address1, "!=",address2
+
+if address1 == address3 : print "test 2 : ", address1, "==",address3
+else : print "test 2 : ", address1, "!=",address3
+
+
+
diff --git a/pydhcplib/examples/server_example.py b/pydhcplib/examples/server_example.py
new file mode 100644 (file)
index 0000000..1a079ab
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+#
+# pydhcplib
+# Copyright (C) 2005 Mathieu Ignacio -- mignacio@april.org
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+from pydhcplib.dhcp_packet import *
+from pydhcplib.dhcp_network import *
+
+
+netopt = {'client_listen_port':"68",
+           'server_listen_port':"67",
+           'listen_address':"0.0.0.0"}
+
+class Server(DhcpServer):
+    def __init__(self, options):
+        DhcpServer.__init__(self,options["listen_address"],
+                            options["client_listen_port"],
+                            options["server_listen_port"])
+        
+    def HandleDhcpDiscover(self, packet):
+        packet.PrintHeaders()
+        packet.PrintOptions()
+        
+    def HandleDhcpRequest(self, packet):
+        packet.PrintHeaders()
+        packet.PrintOptions()
+
+    def HandleDhcpDecline(self, packet):
+        packet.PrintHeaders()
+        packet.PrintOptions()
+        
+    def HandleDhcpRelease(self, packet):
+        packet.PrintHeaders()
+        packet.PrintOptions()
+        
+    def HandleDhcpInform(self, packet):
+        packet.PrintHeaders()
+        packet.PrintOptions()
+
+
+
+server = Server(netopt)
+
+while True :
+    server.GetNextDhcpPacket()
diff --git a/pydhcplib/examples/strlist_example.py b/pydhcplib/examples/strlist_example.py
new file mode 100755 (executable)
index 0000000..9a597ad
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/python
+
+from pydhcplib.type_strlist import strlist
+
+
+word = strlist()
+print "a0 : ",word
+
+word1 = strlist("azerty")
+print "a1 : ",word1
+
+word2 = strlist("qwerty")
+print "a2 : ",word2
+
+word3 = strlist([97, 122, 101, 114, 116, 121])
+print "a3 : ",word3
+
+if word1 == word2 : print "test 1 : ",word1, "==",word2
+else : print "test 1 : " ,word1, "!=",word2
+
+if word1 == word3 : print "test 2 : ", word1, "==",word3
+else : print "test 2 : ", word1, "!=",word3
+
+
+
diff --git a/pydhcplib/man/fr/man3/pydhcplib.3.gz b/pydhcplib/man/fr/man3/pydhcplib.3.gz
new file mode 100644 (file)
index 0000000..28bbf2b
Binary files /dev/null and b/pydhcplib/man/fr/man3/pydhcplib.3.gz differ
diff --git a/pydhcplib/man/fr/man3/pydhcplib.DhcpBasicPacket.3.gz b/pydhcplib/man/fr/man3/pydhcplib.DhcpBasicPacket.3.gz
new file mode 100644 (file)
index 0000000..1d0037c
Binary files /dev/null and b/pydhcplib/man/fr/man3/pydhcplib.DhcpBasicPacket.3.gz differ
diff --git a/pydhcplib/man/fr/man3/pydhcplib.DhcpPacket.3.gz b/pydhcplib/man/fr/man3/pydhcplib.DhcpPacket.3.gz
new file mode 100644 (file)
index 0000000..3ecf004
Binary files /dev/null and b/pydhcplib/man/fr/man3/pydhcplib.DhcpPacket.3.gz differ
diff --git a/pydhcplib/man/fr/man3/pydhcplib.hwmac.3.gz b/pydhcplib/man/fr/man3/pydhcplib.hwmac.3.gz
new file mode 100644 (file)
index 0000000..a1c06e6
Binary files /dev/null and b/pydhcplib/man/fr/man3/pydhcplib.hwmac.3.gz differ
diff --git a/pydhcplib/man/fr/man3/pydhcplib.ipv4.3.gz b/pydhcplib/man/fr/man3/pydhcplib.ipv4.3.gz
new file mode 100644 (file)
index 0000000..8b4f72d
Binary files /dev/null and b/pydhcplib/man/fr/man3/pydhcplib.ipv4.3.gz differ
diff --git a/pydhcplib/man/fr/man3/pydhcplib.strlist.3.gz b/pydhcplib/man/fr/man3/pydhcplib.strlist.3.gz
new file mode 100644 (file)
index 0000000..fbe73a6
Binary files /dev/null and b/pydhcplib/man/fr/man3/pydhcplib.strlist.3.gz differ
diff --git a/pydhcplib/man/fr/man8/pydhcp.8.gz b/pydhcplib/man/fr/man8/pydhcp.8.gz
new file mode 100644 (file)
index 0000000..e2ce9e1
Binary files /dev/null and b/pydhcplib/man/fr/man8/pydhcp.8.gz differ
diff --git a/pydhcplib/man/man3/pydhcplib.3.gz b/pydhcplib/man/man3/pydhcplib.3.gz
new file mode 100644 (file)
index 0000000..9e50336
Binary files /dev/null and b/pydhcplib/man/man3/pydhcplib.3.gz differ
diff --git a/pydhcplib/man/man3/pydhcplib.ipv4.3.gz b/pydhcplib/man/man3/pydhcplib.ipv4.3.gz
new file mode 100644 (file)
index 0000000..123dda4
Binary files /dev/null and b/pydhcplib/man/man3/pydhcplib.ipv4.3.gz differ
diff --git a/pydhcplib/man/man3/pydhcplib.strlist.3.gz b/pydhcplib/man/man3/pydhcplib.strlist.3.gz
new file mode 100644 (file)
index 0000000..a6695eb
Binary files /dev/null and b/pydhcplib/man/man3/pydhcplib.strlist.3.gz differ
diff --git a/pydhcplib/man/man8/pydhcp.8.gz b/pydhcplib/man/man8/pydhcp.8.gz
new file mode 100644 (file)
index 0000000..0d27962
Binary files /dev/null and b/pydhcplib/man/man8/pydhcp.8.gz differ
diff --git a/pydhcplib/pydhcplib/__init__.py b/pydhcplib/pydhcplib/__init__.py
new file mode 100644 (file)
index 0000000..b650ceb
--- /dev/null
@@ -0,0 +1 @@
+__version__ = '0.2'
diff --git a/pydhcplib/pydhcplib/dhcp_basic_packet.py b/pydhcplib/pydhcplib/dhcp_basic_packet.py
new file mode 100644 (file)
index 0000000..26a7254
--- /dev/null
@@ -0,0 +1,188 @@
+# Anemon Dhcp
+# Copyright (C) 2005 Mathieu Ignacio -- mignacio@april.org
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+import operator
+from struct import unpack
+from struct import pack
+from dhcp_constants import *
+
+# DhcpPacket : base class to encode/decode dhcp packets.
+
+
+class DhcpBasicPacket:
+    def __init__(self):
+        self.packet_data = [0]*240
+        self.options_data = {}
+        self.packet_data[236:240] = MagicCookie
+
+    def IsDhcpPacket(self):
+        if self.packet_data[236:240] != MagicCookie : return False
+        return True
+
+    # Check if variable is a list with int between 0 and 255
+    def CheckType(self,variable):
+        if type(variable) == list :
+            for each in variable :
+                if (type(each) != int)  or (each < 0) or (each > 255) :
+                    return False
+            return True
+        else : return False
+        
+
+    def DeleteOption(self,name):
+        # if name is a standard dhcp field
+        # Set field to 0
+        if DhcpFields.has_key(name) :
+            begin = DhcpFields[name][0]
+            end = DhcpFields[name][0]+DhcpFields[name][1]
+            self.packet_data[begin:end] = [0]*DhcpFields[name][1]
+            return True
+
+        # if name is a dhcp option
+        # delete option from self.option_data
+        elif self.options_data.has_key(name) :
+            # forget how to remove a key... try delete
+            self.options_data.__delitem__(name)
+            return True
+
+        return False
+
+    def GetOption(self,name):
+        if DhcpFields.has_key(name) :
+            option_info = DhcpFields[name]
+            return self.packet_data[option_info[0]:option_info[0]+option_info[1]]
+
+        elif self.options_data.has_key(name) :
+            return self.options_data[name]
+
+        return []
+        
+
+    def SetOption(self,name,value):
+
+        # Basic vlue checking :
+        # has value list a correct length
+        
+        # if name is a standard dhcp field
+        if DhcpFields.has_key(name) :
+            if len(value) != DhcpFields[name][1] :
+                print "Error, bad option length (a): ", name
+                return False
+            begin = DhcpFields[name][0]
+            end = DhcpFields[name][0]+DhcpFields[name][1]
+            self.packet_data[begin:end] = value
+            return True
+
+        # if name is a dhcp option
+        elif DhcpOptions.has_key(name) :
+
+            # fields_specs : {'option_code',fixed_length,minimum_length,multiple}
+            # if fixed_length == 0 : minimum_length and multiple apply
+            # else : forget minimum_length and multiple 
+            # multiple : length MUST be a multiple of 'multiple'
+            fields_specs = { "ipv4":[4,0,1], "ipv4+":[0,4,4],
+                             "string":[0,0,1], "bool":[1,0,1],
+                             "char":[1,0,1], "16-bits":[2,0,1],
+                             "32-bits":[4,0,1], "identifier":[0,2,1]}
+            
+            specs = fields_specs[DhcpOptionsTypes[DhcpOptions[name]]]
+            length = len(value)
+            if (specs[0]!=0 and specs==length) or (specs[1]<=length and length%specs[2]==0):
+                self.options_data[name] = value
+                return True
+            else :
+                return False
+
+        print "Error, unknown option : ", name, value
+        return False
+
+
+
+    def IsOption(self,name):
+        if self.options_data.has_key(name) : return True
+        elif DhcpFields.has_key(name) : return True
+        else : return False
+
+
+    # Encode Packet and return it
+    def EncodePacket(self):
+        options = []
+        
+        
+        for each in self.options_data.keys() :
+            options.append(DhcpOptions[each])
+            options.append(len(self.options_data[each]))
+            options += self.options_data[each]
+
+        packet = self.packet_data[:240] + options
+        packet.append(255) # add end option
+        pack_fmt = str(len(packet))+"c"
+
+        packet = map(chr,packet)
+        
+        return pack(pack_fmt,*packet)
+
+
+    # Insert packet in the object
+    def DecodePacket(self,data,debug=False):
+        self.packet_data = []
+        self.options_data = {}
+
+        if (not data) : return False
+        # we transform all data to int list
+        unpack_fmt = str(len(data)) + "c"
+        for i in unpack(unpack_fmt,data):
+            self.packet_data.append(ord(i))
+        if ( debug ) : print "Packet length : ",len(self.packet_data)
+
+
+        # Some servers or clients don't place magic cookie immediately
+        # after headers and begin options fields only after magic.
+        # These 4 lines search magic cookie and begin iterator after.
+        iterator = 236
+        end_iterator = len(self.packet_data)
+        while ( self.packet_data[iterator:iterator+4] != MagicCookie and iterator < end_iterator) :
+            iterator += 1
+        iterator += 4
+        
+        # parse extended options
+        if ( debug ) : print "Debug : ", self.packet_data[iterator:-1]
+
+
+        while iterator < end_iterator :
+            if ( debug ) :
+                print "Debug Option : ", iterator , self.packet_data[iterator]," : ",DhcpOptionsList[self.packet_data[iterator]]
+            if self.packet_data[iterator] == 0 : # pad option
+                opt_first = iterator+1
+                iterator += 1
+
+            elif self.packet_data[iterator] == 255 :
+                self.packet_data = self.packet_data[:240] # base packet length without magic cookie
+                return
+                
+            elif DhcpOptionsTypes.has_key(self.packet_data[iterator]) and self.packet_data[iterator]!= 255:
+                opt_len = self.packet_data[iterator+1]
+                opt_first = iterator+1
+                self.options_data[DhcpOptionsList[self.packet_data[iterator]]] = self.packet_data[opt_first+1:opt_len+opt_first+1]
+                iterator += self.packet_data[opt_first] + 2
+            else :
+                opt_first = iterator+1
+                iterator += self.packet_data[opt_first] + 2
+
+        # cut packet_data to remove options
+        
+        self.packet_data = self.packet_data[:240] # base packet length with magic cookie
diff --git a/pydhcplib/pydhcplib/dhcp_constants.py b/pydhcplib/pydhcplib/dhcp_constants.py
new file mode 100644 (file)
index 0000000..295aa78
--- /dev/null
@@ -0,0 +1,404 @@
+# Anemon Dhcp
+# Copyright (C) 2005 Mathieu Ignacio -- mignacio@april.org
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+
+MagicCookie = [99,130,83,99]
+
+
+# DhcpBaseOptions = '{fieldname':[location,length]}
+DhcpFields = {'op':[0,1],
+                     'htype':[1,1],
+                     'hlen':[2,1],
+                     'hops':[3,1],
+                     'xid':[4,4],
+                     'secs':[8,2],
+                     'flags':[10,2],
+                     'ciaddr':[12,4],
+                     'yiaddr':[16,4],
+                     'siaddr':[20,4],
+                     'giaddr':[24,4],
+                     'chaddr':[28,16],
+                     'sname':[44,64],
+                     'file':[108,128]
+                     }
+DhcpFieldsTypes = {'op':"int",
+                     'htype':"int",
+                     'hlen':"int",
+                     'hops':"int",
+                     'xid':"int4",
+                     'secs':"int2",
+                     'flags':"int2",
+                     'ciaddr':"ipv4",
+                     'yiaddr':"ipv4",
+                     'siaddr':"ipv4",
+                     'giaddr':"ipv4",
+                     'chaddr':"hwmac",
+                     'sname':"str",
+                     'file':"str"
+                     }
+
+# DhcpOptions = 'option_name':option_code
+DhcpOptions = {'pad':0,
+
+               # Vendor Extension
+               'subnet_mask':1,'time_offset':2,
+               'router':3,'time_server':4,'name_server':5,
+               'domain_name_server':6,'log_server':7,
+               'cookie_server':8,'lpr_server':9,
+               'impress_server':10,'resource_location_server':11,
+               'host_name':12,'boot_file':13,'merit_dump_file':14,
+               'domain_name':15,'swap_server':16,'root_path':17,'extensions_path':18,
+
+               # IP layer parameters per host
+               'ip_forwarding':19,'nonlocal_source_rooting':20,
+               'policy_filter':21,'maximum_datagram_reassembly_size':22,
+               'default_ip_time-to-live':23,'path_mtu_aging_timeout':24,
+               'path_mtu_table':25,
+
+               # IP layer parameters per interface
+               'interface_mtu':26,'all_subnets_are_local':27,
+               'broadcast_address':28,'perform_mask_discovery':29,
+               'mask_supplier':30,'perform_router_discovery':31,
+               'routeur_solicitation_address':32,'static_route':33,
+
+               # link layer parameters per interface
+               'trailer_encapsulation':34,'arp_cache_timeout':35,
+               'ethernet_encapsulation':36,
+
+               # TCP parameters
+               'tcp_default_ttl':37,'tcp_keepalive_interval':38,
+               'tcp_keepalive_garbage':39,
+
+               # Applications and service parameters
+               'nis_domain':40,
+               'nis_servers':41,
+               'ntp_servers':42,
+               'vendor_specific_information':43,
+               'nbns':44,
+               'nbdd':45,'node_type':46,
+               'netbios_scope':47,'x_window_system_font_server':48,
+               'x_window_system_display_manager':49,
+
+               # DHCP extensions
+               'request_ip_address':50,
+               'ip_address_lease_time':51,
+               'overload':52,
+               'dhcp_message_type':53,
+               'server_identifier':54,
+               'parameter_request_list':55,
+               'message':56,
+               'maximum_dhcp_message_size':57,
+               'renewal_time_value':58,
+               'rebinding_time_value':59,
+               'class_identifier':60,
+               'client_identifier':61,
+
+               # Add from RFC 2132 
+               'netware_ip_domain_name':62,
+               'netware_ip_sub_options':63,
+               
+               'nis+_domain':64,
+               'nis+_servers':65,
+               'tftp_server_name':66,
+               'bootfile_name':67,
+               'mobile_ip_home_agent':68,
+               'smtp_servers':69,
+               'pop_servers':70,
+               'nntp_servers':71,
+               'default_world_wide_web_server':72,
+               'default_finger_server':73,
+               'default_internet_relay_chat_server':74,
+               'streettalk_server':75,
+               'streettalk_directory_assistance_server':76,
+
+               'user_class':77,
+               'directory_agent':78,
+               'service_scope':79,
+               'rapid_commit':80,
+
+               'client_fqdn':81,
+               'relay_agent':82,
+               'internet_storage_name_service':83,
+               '84':84,
+               'nds_server':85,
+               'nds_tree_name':86,
+               'nds_context':87,
+               '88':88,
+               '89':89,
+               'authentication':90,
+               'client_last_transaction_time':91,
+               'associated_ip':92,
+               'client_system':93,
+               'client_ndi':94,
+               'ldap':95,
+               'unassigned':96,
+               'uuid_guid':97,
+               'open_group_user_auth':98,
+               'unassigned':99,
+               'unassigned':100,
+               'unassigned':101,
+               'unassigned':102,
+               'unassigned':103,
+               'unassigned':104,
+               'unassigned':105,
+               'unassigned':106,
+               'unassigned':107,
+               'unassigned':108,
+               'unassigned':109,
+               'unassigned':110,
+               'unassigned':111,
+               'netinfo_address':112,
+               'netinfo_tag':113,
+               'url':114,
+               'unassigned':115,
+               'auto_config':116,
+               'name_service_search':117,
+               'subnet_selection':118,
+               'domain_search':119,
+               'sip_servers':120,
+               'classless_static_route':121,
+               'cablelabs_client_configuration':122,
+               'geoconf':123,
+               'vendor_class':124,
+               'vendor_specific':125,
+               '126':126,'127':127,'128':128,'129':129,
+               '130':130,'131':131,'132':132,'133':133,
+               '134':134,'135':135,'136':136,'137':137,
+               '138':138,'139':139,'140':140,'141':141,
+               '142':142,'143':143,'144':144,'145':145,
+               '146':146,'147':147,'148':148,'149':149,
+               '150':150,'151':151,'152':152,'153':153,
+               '154':154,'155':155,'156':156,'157':157,
+               '158':158,'159':159,'160':160,'161':161,
+               '162':162,'163':163,'164':164,'165':165,
+               '166':166,'167':167,'168':168,'169':169,
+               '170':170,'171':171,'172':172,'173':173,
+               '174':174,'175':175,'176':176,'177':177,
+               '178':178,'179':179,'180':180,'181':181,
+               '182':182,'183':183,'184':184,'185':185,
+               '186':186,'187':187,'188':188,'189':189,
+               '190':190,'191':191,'192':192,'193':193,
+               '194':194,'195':195,'196':196,'197':197,
+               '198':198,'199':199,'200':200,'201':201,
+               '202':202,'203':203,'204':204,'205':205,
+               '206':206,'207':207,'208':208,'209':209,
+               '210':210,'211':211,'212':212,'213':213,
+               '214':214,'215':215,'216':216,'217':217,
+               '218':218,'219':219,'220':220,'221':221,
+               '222':222,'223':223,'224':224,'225':225,
+               '226':226,'227':227,'228':228,'229':229,
+               '230':230,'231':231,'232':232,'233':233,
+               '234':234,'235':235,'236':236,'237':237,
+               '238':238,'239':239,'240':240,'241':241,
+               '242':242,'243':243,'244':244,'245':245,
+               '246':246,'247':247,'248':248,'249':249,
+               '250':250,'251':251,'252':252,'253':253,
+               '254':254,'end':255
+
+               }
+
+# DhcpOptionsList : reverse of DhcpOptions
+DhcpOptionsList = ['pad',
+
+                   # Vendor Extension
+                   'subnet_mask','time_offset',
+                   'router','time_server','name_server',
+                   'domain_name_server','log_server',
+                   'cookie_server','lpr_server',
+                   'impress_server','resource_location_server',
+                   'host_name','boot_file','merit_dump_file',
+                   'domain_name','swap_server','root_path','extensions_path',
+                   
+                   # IP layer parameters per host
+                   'ip_forwarding','nonlocal_source_rooting',
+                   'policy_filter','maximum_datagram_reassembly_size',
+                   'default_ip_time-to-live','path_mtu_aging_timeout',
+                   'path_mtu_table',
+                   
+                   # IP layer parameters per interface
+                   'interface_mtu','all_subnets_are_local',
+                   'broadcast_address','perform_mask_discovery',
+                   'mask_supplier','perform_router_discovery',
+                   'routeur_solicitation_address','static_route',
+                   
+                   # link layer parameters per interface
+                   'trailer_encapsulation','arp_cache_timeout',
+                   'ethernet_encapsulation',
+                   
+                   # TCP parameters
+                   'tcp_default_ttl','tcp_keepalive_interval',
+                   'tcp_keepalive_garbage',
+                   
+                   # Applications and service parameters
+                   'nis_domain',
+                   'nis_servers',
+                   'ntp_servers',
+                   'vendor_specific_information','nbns',
+                   'nbdd','node_type',
+                   'netbios_scope','x_window_system_font_server',
+                   'x_window_system_display_manager',
+
+                   # DHCP extensions
+                   'request_ip_address',
+                   'ip_address_lease_time',
+                   'overload',
+                   'dhcp_message_type',
+                   'server_identifier',
+                   'parameter_request_list',
+                   'message',
+                   'maximum_dhcp_message_size',
+                   'renewal_time_value',
+                   'rebinding_time_value',
+                   'class_identifier',
+                   'client_identifier',
+                   
+
+                   # adds from RFC 2132,2242
+                   'netware_ip_domain_name',
+                   'netware_ip_sub_options',
+                   'nis+_domain',
+                   'nis+_servers',
+                   'tftp_server_name',
+                   'bootfile_name',
+                   'mobile_ip_home_agent',
+                   'smtp_servers',
+                   'pop_servers',
+                   'nntp_servers',
+                   'default_world_wide_web_server',
+                   'default_finger_server',
+                   'default_internet_relay_chat_server',
+                   'streettalk_server',
+                   'streettalk_directory_assistance_server',
+                   'user_class','directory_agent','service_scope',
+
+                   # 80
+                   'rapid_commit','client_fqdn','relay_agent',
+                   'internet_storage_name_service',
+                   '84',
+                   'nds_server','nds_tree_name','nds_context',
+                   '88','89',
+
+                   #90
+                   'authentication',
+                   'client_last_transaction_time','associated_ip', #RFC 4388
+                   'client_system', 'client_ndi', #RFC 3679
+                   'ldap','unassigned','uuid_guid', #RFC 3679
+                   'open_group_user_auth', #RFC 2485
+
+                   # 99->115 RFC3679 
+                   'unassigned','unassigned','unassigned',
+                   'unassigned','unassigned','unassigned',
+                   'unassigned','unassigned','unassigned',
+                   'unassigned','unassigned','unassigned',
+                   'unassigned','netinfo_address','netinfo_tag',
+                   'url','unassigned',
+
+                   #116
+                   'auto_config','name_service_search','subnet_selection',
+                   'domain_search','sip_servers','classless_static_route',
+                   'cablelabs_client_configuration','geoconf',
+
+                   #124
+                   'vendor_class', 'vendor_specific',
+
+                   '126','127','128','129',
+                   '130','131','132','133','134','135','136','137','138','139',
+                   '140','141','142','143','144','145','146','147','148','149',
+                   '150','151','152','153','154','155','156','157','158','159',
+                   '160','161','162','163','164','165','166','167','168','169',
+                   '170','171','172','173','174','175','176','177','178','179',
+                   '180','181','182','183','184','185','186','187','188','189',
+                   '190','191','192','193','194','195','196','197','198','199',
+                   '200','201','202','203','204','205','206','207','208','209',
+                   '210','211','212','213','214','215','216','217','218','219',
+                   '220','221','222','223','224','225','226','227','228','229',
+                   '230','231','232','233','234','235','236','237','238','239',
+                   '240','241','242','243','244','245','246','247','248','249',
+                   '250','251','252','253','254',
+
+                   'end'
+                   ] 
+
+
+# See http://www.iana.org/assignments/bootp-dhcp-parameters
+# FIXME : verify all ipv4+ options, somes are 32 bits...
+
+DhcpOptionsTypes = {0:"none", 1:"ipv4", 2:"ipv4", 3:"ipv4+", 
+                    4:"ipv4+", 5:"ipv4+", 6:"ipv4+", 7:"ipv4+", 
+                    8:"ipv4+", 9:"ipv4+", 10:"ipv4+", 11:"ipv4+", 
+                    12:"string", 13:"16-bits", 14:"string", 15:"string", 
+                    16:"ipv4", 17:"string", 18:"string", 19:"bool", 
+                    20:"bool", 21:"ipv4+", 22:"16-bits", 23:"char", 
+                    24:"ipv4", 25:"16-bits", 26:"16-bits", 27:"bool", 
+                    28:"ipv4", 29:"bool", 30:"bool", 31:"bool", 
+                    32:"ipv4", 33:"ipv4+", 34:"bool", 35:"32-bits", 
+                    36:"bool", 37:"char", 38:"32-bits", 39:"bool", 
+                    40:"string", 41:"ipv4+", 42:"ipv4+", 43:"string", 
+                    44:"ipv4+", 45:"ipv4+", 46:"char", 47:"string", 
+                    48:"ipv4+", 49:"ipv4+", 50:"ipv4", 51:"32-bits", 
+                    52:"char", 53:"char", 54:"ipv4", 55:"none", 
+                    56:"string", 57:"16-bits", 58:"32-bits", 59:"32-bits", 
+                    60:"string", 61:"identifier", 62:"string", 63:"RFC2242", 
+                    64:"string", 65:"ipv4+", 66:"string", 67:"string", 
+                    68:"ipv4", 69:"ipv4+", 70:"ipv4+", 71:"ipv4+",                     
+                    72:"ipv4+", 73:"ipv4+", 74:"ipv4+", 75:"ipv4+", 
+                    76:"ipv4+", 77:"RFC3004", 78:"RFC2610", 79:"RFC2610", 
+                    80:"null", 81:"string", 82:"RFC3046", 83:"RFC4174", 
+                    84:"Unassigned", 85:"ipv4+", 86:"RFC2241", 87:"RFC2241", 
+                    88:"Unassigned", 89:"Unassigned", 90:"RFC3118", 91:"RFC4388", 
+                    92:"ipv4+", 93:"Unassigned", 94:"Unassigned", 95:"Unassigned", 
+                    96:"Unassigned", 97:"Unassigned", 98:"string", 99:"Unassigned", 
+                    100:"Unassigned", 101:"Unassigned", 102:"Unassigned", 103:"Unassigned", 
+                    104:"Unassigned", 105:"Unassigned", 106:"Unassigned", 107:"Unassigned", 
+                    108:"Unassigned", 109:"Unassigned", 110:"Unassigned", 111:"Unassigned", 
+                    112:"Unassigned", 113:"Unassigned", 114:"Unassigned", 115:"Unassigned", 
+                    116:"char", 117:"RFC2937", 118:"ipv4", 119:"RFC3397", 
+                    120:"RFC3361",
+
+                    #TODO
+                    121:"Unassigned", 122:"Unassigned", 123:"Unassigned", 
+                    124:"Unassigned", 125:"Unassigned", 126:"Unassigned", 127:"Unassigned", 
+                    128:"Unassigned", 129:"Unassigned", 130:"Unassigned", 131:"Unassigned", 
+                    132:"Unassigned", 133:"Unassigned", 134:"Unassigned", 135:"Unassigned", 
+                    136:"Unassigned", 137:"Unassigned", 138:"Unassigned", 139:"Unassigned", 
+                    140:"Unassigned", 141:"Unassigned", 142:"Unassigned", 143:"Unassigned", 
+                    144:"Unassigned", 145:"Unassigned", 146:"Unassigned", 147:"Unassigned", 
+                    148:"Unassigned", 149:"Unassigned", 150:"Unassigned", 151:"Unassigned", 
+                    152:"Unassigned", 153:"Unassigned", 154:"Unassigned", 155:"Unassigned", 
+                    156:"Unassigned", 157:"Unassigned", 158:"Unassigned", 159:"Unassigned", 
+                    160:"Unassigned", 161:"Unassigned", 162:"Unassigned", 163:"Unassigned", 
+                    164:"Unassigned", 165:"Unassigned", 166:"Unassigned", 167:"Unassigned", 
+                    168:"Unassigned", 169:"Unassigned", 170:"Unassigned", 171:"Unassigned", 
+                    172:"Unassigned", 173:"Unassigned", 174:"Unassigned", 175:"Unassigned", 
+                    176:"Unassigned", 177:"Unassigned", 178:"Unassigned", 179:"Unassigned", 
+                    180:"Unassigned", 181:"Unassigned", 182:"Unassigned", 183:"Unassigned", 
+                    184:"Unassigned", 185:"Unassigned", 186:"Unassigned", 187:"Unassigned", 
+                    188:"Unassigned", 189:"Unassigned", 190:"Unassigned", 191:"Unassigned", 
+                    192:"Unassigned", 193:"Unassigned", 194:"Unassigned", 195:"Unassigned", 
+                    196:"Unassigned", 197:"Unassigned", 198:"Unassigned", 199:"Unassigned", 
+                    200:"Unassigned", 201:"Unassigned", 202:"Unassigned", 203:"Unassigned", 
+                    204:"Unassigned", 205:"Unassigned", 206:"Unassigned", 207:"Unassigned", 
+                    208:"Unassigned", 209:"Unassigned", 210:"Unassigned", 211:"Unassigned", 
+                    212:"Unassigned", 213:"Unassigned", 214:"Unassigned", 215:"Unassigned", 
+                    216:"Unassigned", 217:"Unassigned", 218:"Unassigned", 219:"Unassigned", 
+                    220:"Unassigned", 221:"Unassigned", 222:"Unassigned", 223:"Unassigned", 
+                    224:"Unassigned", 225:"Unassigned", 226:"Unassigned", 227:"Unassigned", 
+                    228:"Unassigned", 229:"Unassigned", 230:"Unassigned", 231:"Unassigned", 
+                    232:"Unassigned", 233:"Unassigned", 234:"Unassigned", 235:"Unassigned", 
+                    236:"Unassigned", 237:"Unassigned", 238:"Unassigned", 239:"Unassigned", 
+                    240:"Unassigned", 241:"Unassigned", 242:"Unassigned", 243:"Unassigned", 
+                    244:"Unassigned", 245:"Un"}
diff --git a/pydhcplib/pydhcplib/dhcp_network.py b/pydhcplib/pydhcplib/dhcp_network.py
new file mode 100644 (file)
index 0000000..5e95707
--- /dev/null
@@ -0,0 +1,134 @@
+# pydhcplib
+# Copyright (C) 2005,2006 Mathieu Ignacio -- mignacio@april.org
+#
+# This file is part of pydhcplib.
+# Pydhcplib is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+import sys
+import socket
+import dhcp_packet
+
+
+class DhcpNetwork:
+    def __init__(self, listen_address="0.0.0.0", listen_port=67, emit_port=68):
+
+        self.listen_port = int(listen_port)
+        self.emit_port = int(emit_port)
+        self.listen_address = listen_address
+        
+    def GetNextDhcpPacket(self):
+        data =""
+
+        while data == "" :
+            data = self.dhcp_socket.recv(1024)
+            if data != "" :
+                packet = dhcp_packet.DhcpPacket()
+                packet.DecodePacket(data)
+
+                self.HandleDhcpAll(packet)
+                
+                if packet.IsDhcpDiscoverPacket():
+                    self.HandleDhcpDiscover(packet)
+                elif packet.IsDhcpRequestPacket():
+                    self.HandleDhcpRequest(packet)
+                elif packet.IsDhcpDeclinePacket():
+                    self.HandleDhcpDecline(packet)
+                elif packet.IsDhcpReleasePacket():
+                    self.HandleDhcpRelease(packet)
+                elif packet.IsDhcpInformPacket():
+                    self.HandleDhcpInform(packet)
+                elif packet.IsDhcpOfferPacket():
+                    self.HandleDhcpOffer(packet)
+                elif packet.IsDhcpAckPacket():
+                    self.HandleDhcpAck(packet)
+                elif packet.IsDhcpNackPacket():
+                    self.HandleDhcpNack(packet)
+                else: self.HandleDhcpUnknown(packet)
+
+                return packet
+
+
+    def SendDhcpPacketTo(self, To, packet):
+        return self.dhcp_socket.sendto(packet.EncodePacket(),(To,self.emit_port))
+
+
+    # Server side Handle methods
+    def HandleDhcpDiscover(self, packet):
+        print "HandleDhcpRequest : method not implemented"
+
+    def HandleDhcpRequest(self, packet):
+        print "HandleDhcpRequest : method not implemented"
+
+    def HandleDhcpDecline(self, packet):
+        print "HandleDhcpDecline : method not implemented"
+
+    def HandleDhcpRelease(self, packet):
+        print "HandleDhcpRelease : method not implemented"
+
+    def HandleDhcpInform(self, packet):
+        print "HandleDhcpInform : method not implemented"
+
+
+    # client-side Handle methods
+    def HandleDhcpOffer(self, packet):
+        print "HandleDhcpOffer : method not implemented"
+        
+    def HandleDhcpAck(self, packet):
+        print "HandleDhcpAckhandling : method not implemented"
+
+    def HandleDhcpNack(self, packet):
+        print "HandleDhcpNack : method not implemented"
+
+
+    # Handle unknown options or all options
+    def HandleDhcpUnknown(self, packet):
+        print "HandleDhcpUnknown : method not implemented"
+
+    def HandleDhcpAll(self, packet):
+        pass
+
+
+class DhcpServer(DhcpNetwork) :
+    def __init__(self, listen_address="0.0.0.0", client_listen_port=67,server_listen_port=68) :
+        
+        DhcpNetwork.__init__(self,listen_address,server_listen_port,client_listen_port)
+        
+        self.dhcp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+        self.dhcp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_BROADCAST,1)
+        self.dhcp_socket.bind((self.listen_address, self.listen_port))
+
+
+class DhcpClient(DhcpNetwork) :
+    def __init__(self, listen_address="0.0.0.0",client_listen_port=67,server_listen_port=68) :
+
+        DhcpNetwork.__init__(self,listen_address,client_listen_port,server_listen_port)
+
+        self.dhcp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+        self.dhcp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_BROADCAST,1)
+        self.dhcp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
+        self.dhcp_socket.bind((self.listen_address, self.listen_port))
+
+# Raw client permit to listen on network even if there is
+# no interface set. Probably useful... :-)
+class DhcpRawClient(DhcpNetwork) :
+    def __init__(self, interface="eth0", client_listen_port=67,server_listen_port=68) :
+
+        DhcpNetwork.__init__(self,interface,client_listen_port,server_listen_port)
+        print interface
+                # 0x800 : ETH_P_IP, 0x003 : ETH_P_ALL
+        # See Linux/if_ether.h 
+        self.dhcp_socket = socket.socket(socket.AF_PACKET, socket.SOCK_DGRAM,socket.ntohs(0x0800))
+
+            
diff --git a/pydhcplib/pydhcplib/dhcp_packet.py b/pydhcplib/pydhcplib/dhcp_packet.py
new file mode 100644 (file)
index 0000000..4f564f7
--- /dev/null
@@ -0,0 +1,221 @@
+# Anemon Dhcp
+# Copyright (C) 2005 Mathieu Ignacio -- mignacio@april.org
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+import operator
+from struct import unpack
+from struct import pack
+from dhcp_basic_packet import *
+from dhcp_constants import *
+from type_ipv4 import ipv4
+from type_strlist import strlist
+class DhcpPacket(DhcpBasicPacket):
+
+
+    # Useful function for debugging
+    def PrintHeaders(self):
+        print "# Header fields\n"
+        print "readable_dhcp_headers = {"
+        for opt in  ['op','htype','hlen','hops','xid','secs','flags',
+                     'ciaddr','yiaddr','siaddr','giaddr','chaddr','sname','file'] :
+            begin = DhcpFields[opt][0]
+            end = DhcpFields[opt][0]+DhcpFields[opt][1]
+            data = self.packet_data[begin:end]
+            if DhcpFieldsTypes[opt] == "int" : result = str(data[0])
+            if DhcpFieldsTypes[opt] == "int2" : result = str(data[0]*256+data[0])
+            if DhcpFieldsTypes[opt] == "int4" : result = str(ipv4(data).int())
+            if DhcpFieldsTypes[opt] == "str" : result = strlist(data).str()
+            if DhcpFieldsTypes[opt] == "ipv4" : result = ipv4(data).str()
+            if DhcpFieldsTypes[opt] == "hwmac" : result = "".join(map(chr,data))
+
+            line = "\t'"+opt+"':"+str(data)+",\t# "+result
+            print line
+        print "\t'end':'true'}"
+
+    # Useful function for debugging
+    def PrintOptions(self):
+        print "# Options fields"
+        print "readable_dhcp_options = {"
+        for opt in self.options_data.keys():
+            data = self.options_data[opt]
+            result = ""
+            optnum  = DhcpOptions[opt]
+            if DhcpOptionsTypes[optnum] == "char" : result = str(data[0])
+            if DhcpOptionsTypes[optnum] == "16-bits" : result = str(data[0]*256+data[0])
+            if DhcpOptionsTypes[optnum] == "32bits" : result = str(ipv4(data).int())
+            if DhcpOptionsTypes[optnum] == "string" : result = strlist(data).str()
+            if DhcpOptionsTypes[optnum] == "ipv4" : result = ipv4(data).str()
+            if DhcpOptionsTypes[optnum] == "ipv4+" :
+                for i in range(0,len(data),4) :
+                    if len(data[i:i+4]) == 4 :
+                        result += ipv4(data[i:i+4]).str() + " - "
+            line = "\t'"+opt+"':"+str(data)+",\t# "+result
+            print line
+        print "\t'end':'true'}"
+        
+
+            
+    # FIXME: This is called from IsDhcpSomethingPacket, but is this really
+    # needed?  Or maybe this testing should be done in
+    # DhcpBasicPacket.DecodePacket().
+
+    # Test Packet Type
+    def IsDhcpSomethingPacket(self,type):
+        if self.IsDhcpPacket() == False : return False
+        if self.IsOption("dhcp_message_type") == False : return False
+        if self.GetOption("dhcp_message_type") != type : return False
+        return True
+    
+    def IsDhcpDiscoverPacket(self):
+        return self.IsDhcpSomethingPacket([1])
+
+    def IsDhcpOfferPacket(self):
+        return self.IsDhcpSomethingPacket([2])
+
+    def IsDhcpRequestPacket(self):
+        return self.IsDhcpSomethingPacket([3])
+
+    def IsDhcpDeclinePacket(self):
+        return self.IsDhcpSomethingPacket([4])
+
+    def IsDhcpAckPacket(self):
+        return self.IsDhcpSomethingPacket([5])
+
+    def IsDhcpNackPacket(self):
+        return self.IsDhcpSomethingPacket([6])
+
+    def IsDhcpReleasePacket(self):
+        return self.IsDhcpSomethingPacket([7])
+
+    def IsDhcpInformPacket(self):
+        return self.IsDhcpSomethingPacket([8])
+
+
+    def GetMultipleOptions(self,options=()):
+        result = {}
+        for each in options:
+            result[each] = self.GetOption(each)
+        return result
+
+    def SetMultipleOptions(self,options={}):
+        for each in options.keys():
+            self.SetOption(each,options[each])
+
+
+
+
+
+
+    # Creating Response Packet
+
+    # Server-side functions
+    # From RFC 2132 page 28/29
+    def CreateDhcpOfferPacketFrom(self,src): # src = discover packet
+        self.SetOption("htype",src.GetOption("htype"))
+        self.SetOption("xid",src.GetOption("xid"))
+        self.SetOption("flags",src.GetOption("flags"))
+        self.SetOption("giaddr",src.GetOption("giaddr"))
+        self.SetOption("chaddr",src.GetOption("chaddr"))
+        self.SetOption("ip_address_lease_time",src.GetOption("ip_address_lease_time"))
+        self.TransformToDhcpOfferPacket()
+
+    def TransformToDhcpOfferPacket(self):
+        self.SetOption("dhcp_message_type",[2])
+        self.SetOption("op",[2])
+        self.SetOption("hlen",[6]) 
+
+        self.DeleteOption("secs")
+        self.DeleteOption("ciaddr")
+        self.DeleteOption("request_ip_address")
+        self.DeleteOption("parameter_request_list")
+        self.DeleteOption("client_identifier")
+        self.DeleteOption("maximum_message_size")
+
+
+
+
+
+    """ Dhcp ACK packet creation """
+    def CreateDhcpAckPacketFrom(self,src): # src = request or inform packet
+        self.SetOption("htype",src.GetOption("htype"))
+        self.SetOption("xid",src.GetOption("xid"))
+        self.SetOption("ciaddr",src.GetOption("ciaddr"))
+        self.SetOption("flags",src.GetOption("flags"))
+        self.SetOption("giaddr",src.GetOption("giaddr"))
+        self.SetOption("chaddr",src.GetOption("chaddr"))
+        self.SetOption("ip_address_lease_time_option",src.GetOption("ip_address_lease_time_option"))
+        self.TransformToDhcpAckPacket()
+
+    def TransformToDhcpAckPacket(self): # src = request or inform packet
+        self.SetOption("op",[2])
+        self.SetOption("hlen",[6]) 
+        self.SetOption("dhcp_message_type",[5])
+
+        self.DeleteOption("secs")
+        self.DeleteOption("request_ip_address")
+        self.DeleteOption("parameter_request_list")
+        self.DeleteOption("client_identifier")
+        self.DeleteOption("maximum_message_size")
+
+
+    """ Dhcp NACK packet creation """
+    def CreateDhcpNackPacketFrom(self,src): # src = request or inform packet
+        
+        self.SetOption("htype",src.GetOption("htype"))
+        self.SetOption("xid",src.GetOption("xid"))
+        self.SetOption("flags",src.GetOption("flags"))
+        self.SetOption("giaddr",src.GetOption("giaddr"))
+        self.SetOption("chaddr",src.GetOption("chaddr"))
+        self.TransformToDhcpNackPacket()
+
+    def TransformToDhcpNackPacket(self):
+        self.SetOption("op",[2])
+        self.SetOption("hlen",[6]) 
+        self.DeleteOption("secs")
+        self.DeleteOption("ciaddr")
+        self.DeleteOption("yiaddr")
+        self.DeleteOption("siaddr")
+        self.DeleteOption("sname")
+        self.DeleteOption("file")
+        self.DeleteOption("request_ip_address")
+        self.DeleteOption("ip_address_lease_time_option")
+        self.DeleteOption("parameter_request_list")
+        self.DeleteOption("client_identifier")
+        self.DeleteOption("maximum_message_size")
+        self.SetOption("dhcp_message_type",[6])
+
+
+
+
+
+
+
+    """ GetClientIdentifier """
+
+    def GetClientIdentifier(self) :
+        if self.IsOption("client_identifier") :
+            return self.GetOption("client_identifier")
+        return []
+
+    def GetGiaddr(self) :
+        return self.GetOption("giaddr")
+
+    def GetHardwareAddress(self) :
+        length = self.GetOption("hlen")[0]
+        full_hw = self.GetOption("chaddr")
+        if length!=[] and length<len(full_hw) : return full_hw[0:length]
+        return full_hw
+
diff --git a/pydhcplib/pydhcplib/type_hw_addr.py b/pydhcplib/pydhcplib/type_hw_addr.py
new file mode 100644 (file)
index 0000000..a6e3d64
--- /dev/null
@@ -0,0 +1,86 @@
+# Anemon Dhcp
+# Copyright (C) 2005 Mathieu Ignacio -- mignacio@april.org
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+
+from binascii import unhexlify,hexlify
+
+# Check and convert hardware/nic/mac address type
+class hwmac:
+    def __init__(self,value="00:00:00:00:00:00") :
+        self._hw_numlist = []
+        self._hw_string = ""
+        hw_type = type(value)
+        if hw_type == str :
+            self._hw_string = value
+            self._StringToNumlist(value)
+            self._CheckNumList()
+        elif hw_type == list :
+            self._hw_numlist = value
+            self._CheckNumList()
+            self._NumlistToString()
+        else : raise TypeError , 'hwmac init : Valid types are  tr and list'
+
+
+
+    # Check if _hw_numlist is valid and raise error if not.
+    def _CheckNumList(self) :
+        if len(self._hw_numlist) != 6 : raise ValueError , "hwmac : wrong list length."
+        for part in self._hw_numlist :
+            if type (part) != int : raise TypeError , "hwmac : each element of list must be int"
+            if part < 0 or part > 255 : raise ValueError , "hwmac : need numbers between 0 and 255."
+        return True
+
+
+    def _StringToNumlist(self,value):
+        self._hw_string = self._hw_string.replace("-",":").replace(".",":")
+        self._hw_string = self._hw_string.lower()
+
+
+        for twochar in self._hw_string.split(":"):
+            self._hw_numlist.append(ord(unhexlify(twochar)))
+            
+    # Convert NumList type ip to String type ip
+    def _NumlistToString(self) :
+        self._hw_string = ":".join(map(hexlify,map(chr,self._hw_numlist)))
+
+    # Convert String type ip to NumList type ip
+    # return ip string
+    def str(self) :
+        return self._hw_string
+    __str__=str
+
+    # return ip list (useful for DhcpPacket class)
+    def list(self) :
+        return self._hw_numlist
+
+    def __hash__(self) :
+        return self._hw_string.__hash__()
+
+    def __repr__(self) :
+        return self._hw_string
+
+    def __cmp__(self,other) :
+        if self._hw_string == other : return 0
+        return 1
+
+    def __nonzero__(self) :
+        if self._hw_string != "00:00:00:00:00:00" : return 1
+        return 0
+
+
+
+
diff --git a/pydhcplib/pydhcplib/type_ipv4.py b/pydhcplib/pydhcplib/type_ipv4.py
new file mode 100644 (file)
index 0000000..f42dec0
--- /dev/null
@@ -0,0 +1,117 @@
+# Anemon Dhcp
+# Copyright (C) 2005 Mathieu Ignacio -- mignacio@april.org
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+
+# Check and convert ipv4 address type
+class ipv4:
+    def __init__(self,value="0.0.0.0") :
+        ip_type = type(value)
+        if ip_type == str :
+            if not self.CheckString(value) : raise ValueError, "ipv4 string argument is not an valid ip "
+            self._ip_string = value
+            self._StringToNumlist()
+            self._StringToLong()
+            self._NumlistToString()
+        elif ip_type == list :
+            if not self.CheckNumList(value) : raise ValueError, "ipv4 list argument is not an valid ip "
+            self._ip_numlist = value
+            self._NumlistToString()
+            self._StringToLong()
+        elif ip_type == int or ip_type == long:
+            self._ip_long = value
+            self._LongToNumlist()
+            self._NumlistToString()
+        elif ip_type == bool :
+            self._ip_long = 0
+            self._LongToNumlist()
+            self._NumlistToString()
+            
+        else : raise TypeError , 'ipv4 init : Valid types are  str, list, int or long'
+
+
+
+    # Convert Long type ip to numlist ip
+    def _LongToNumlist(self) :
+        self._ip_numlist = [self._ip_long >> 24 & 0xFF]
+        self._ip_numlist.append(self._ip_long >> 16 & 0xFF)
+        self._ip_numlist.append(self._ip_long >> 8 & 0xFF)
+        self._ip_numlist.append(self._ip_long & 0xFF)
+        if not self.CheckNumList(self._ip_numlist) : raise ValueError, "ipv4 list argument is not an valid ip "
+    # Convert String type ip to Long type ip
+    def _StringToLong(self) :
+        ip_numlist = map(int,self._ip_string.split('.'))
+        self._ip_long = ip_numlist[3] + ip_numlist[2]*256 + ip_numlist[1]*256*256 + ip_numlist[0]*256*256*256
+        if not self.CheckNumList(self._ip_numlist) : raise ValueError, "ipv4 list argument is not an valid ip "
+    # Convert NumList type ip to String type ip
+    def _NumlistToString(self) :
+        self._ip_string = ".".join(map(str,self._ip_numlist))
+        if not self.CheckNumList(self._ip_numlist) : raise ValueError, "ipv4 list argument is not an valid ip "
+    # Convert String type ip to NumList type ip
+    def _StringToNumlist(self) :
+        self._ip_numlist = map(int,self._ip_string.split('.'))
+        if not self.CheckNumList(self._ip_numlist) : raise ValueError, "ipv4 list argument is not an valid ip "
+
+    """ Public methods """
+    # Check if _ip_numlist is valid and raise error if not.
+    # self._ip_numlist
+    def CheckNumList(self,value) :
+        if len(value) != 4 : return False
+        for part in value :
+            if part < 0 or part > 255 : return False
+        return True
+
+    # Check if _ip_numlist is valid and raise error if not.
+    def CheckString(self,value) :
+        tmp = value.split('.')
+        if len(tmp) != 4 :  return False
+        for each in tmp : 
+            if not each.isdigit() : return False
+        return True
+    
+    # return ip string
+    def str(self) :
+        return self._ip_string
+    __str__=str
+
+    # return ip list (useful for DhcpPacket class)
+    def list(self) :
+        return self._ip_numlist
+
+    # return Long ip type (useful for SQL ip address backend)
+    def int(self) :
+        return self._ip_long
+
+
+
+
+    """ Useful function for native python operations """
+
+    def __hash__(self) :
+        return self._ip_long.__hash__()
+
+    def __repr__(self) :
+        return self._ip_string
+
+    def __cmp__(self,other) :
+        return cmp(self._ip_long, other._ip_long);
+
+    def __nonzero__(self) :
+        if self._ip_long != 0 : return 1
+        return 0
+
+
+
diff --git a/pydhcplib/pydhcplib/type_strlist.py b/pydhcplib/pydhcplib/type_strlist.py
new file mode 100644 (file)
index 0000000..ca5f1c0
--- /dev/null
@@ -0,0 +1,65 @@
+# Anemon Dhcp
+# Copyright (C) 2005 Mathieu Ignacio -- mignacio@april.org
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+class strlist :
+    def __init__(self,data="") :
+        str_type = type(data)
+        self._str = ""
+        self._list = []
+        
+        if str_type == str :
+            self._str = data
+            for each in range(len(self._str)) :
+                self._list.append(ord(self._str[each]))
+        elif str_type == list :
+            self._list = data
+            self._str = "".join(map(chr,self._list))
+        else : raise TypeError , 'strlist init : Valid types are str and  list of int'
+
+    # return string
+    def str(self) :
+        return self._str
+
+    # return list (useful for DhcpPacket class)
+    def list(self) :
+        return self._list
+
+    # return int
+    # FIXME
+    def int(self) :
+        return 0
+
+
+
+    """ Useful function for native python operations """
+
+    def __hash__(self) :
+        return self._str.__hash__()
+
+    def __repr__(self) :
+        return self._str
+
+    def __nonzero__(self) :
+        if self._str != "" : return 1
+        return 0
+
+    def __cmp__(self,other) :
+        if self._str == other : return 0
+        return 1
+                   
+
+
diff --git a/pydhcplib/scripts/pydhcp b/pydhcplib/scripts/pydhcp
new file mode 100644 (file)
index 0000000..a0fec1b
--- /dev/null
@@ -0,0 +1,122 @@
+#!/usr/bin/python
+
+
+from pydhcplib import dhcp_constants
+from pydhcplib import dhcp_packet
+from pydhcplib import dhcp_network
+
+from pydhcplib import type_hw_addr
+from pydhcplib import type_ipv4
+from pydhcplib import type_strlist
+
+import sys
+
+from optparse import OptionParser
+
+
+parser = OptionParser()
+
+""" Action options """
+parser.add_option("-L", "--listen", action="store_true",dest="listen", help="",default=False)
+parser.add_option("-E", "--emit",  action="store_true",dest="emit", help="",  default=False)
+parser.add_option("-R", "--readable-conversion",  action="store_true",dest="readable", help="", default=False)
+parser.add_option("-B", "--binary-conversion",  action="store_true",dest="binary", help="", default=False)
+parser.add_option("-s", "--source-file", action="store",dest="source", help="", default=False, type="string")
+parser.add_option("-d", "--destination-file", action="store",dest="destination", help="", default=False, type="string")
+parser.add_option("-p", "--port", action="store",dest="port", help="", default="67", type="int")
+parser.add_option("-a", "--address", action="store",dest="address", help="", default="0.0.0.0", type="string")
+parser.add_option("-r", "--raw", action="store",dest="raw", help="", default=False,type="string")
+parser.add_option("-n", "--number", action="store",dest="number", help="", default="0", type="int")
+
+
+
+(options, args) = parser.parse_args()
+
+print options
+
+
+
+def main() :
+    ActionSum = 0
+    for Action in (options.listen,options.emit,options.readable,options.binary) :
+        if Action == True : ActionSum += 1
+    if ActionSum > 1 :
+        print "Command line error : [-L -E -R -B] Only one of these actions can be taken."
+        sys.exit(0)
+
+    if options.readable == True : r_conversion()
+
+    if options.binary == True : b_conversion()
+
+    if options.listen == True :
+        if options.raw == False:
+            listen_address(options.address,options.port,int(options.number))
+        else  :
+            print "Listen RAW : ",options.raw
+            listen_packet(options.raw,options.number)
+
+    if options.emit == True : emit(options.address,options.port)
+
+
+def listen_address(address,port,number) :
+    listener = dhcp_network.DhcpClient(address,port,port)
+
+    if  (number == 0 ) :
+        while True :
+            packet = listener.GetNextDhcpPacket()
+            packet.PrintHeaders()
+            packet.PrintOptions()
+
+    else :
+        while number > 0 :
+            packet = listener.GetNextDhcpPacket()
+            packet.PrintHeaders()
+            packet.Print()
+
+            number -= 1
+
+def listen_packet(interface,number) :
+    listener = dhcp_network.DhcpRawClient(mysocket)
+
+    if  (number == 0 ) :
+        while True :
+            packet = dhcp_packet.DhcpPacket()
+            
+            packet.DecodePacket(listener.Receive(1024))
+            packet.PrintHeaders()
+            packet.PrintOptions()
+
+    else :
+        while number > 0 :
+            packet = dhcp_packet.DhcpPacket()
+            
+            packet.DecodePacket(listener.Receive(1024))
+            packet.PrintHeaders()
+            packet.PrintOptions()
+
+            number -= 1
+    
+
+def emit(address,port) :
+    pass
+
+def r_conversion() :
+    rawdata = sys.stdin.read()
+    while ( len(rawdata)>0 ) :
+        readdata = dhcp_packet.DhcpPacket()
+        readdata.DecodePacket(rawdata)
+        readdata.PrintHeaders()
+        readdata.PrintOptions()
+        rawdata = sys.stdin.read()
+
+def b_conversion() :
+    """
+    pythondata =  sys.stdin.read()
+    while ( len(pythondata)>0 ) :
+        data = dhcp_packet.DhcpPacket()
+        data.DecodePacket(rawdata)
+        
+        pythondata = sys.stdin.read()
+    """
+
+main()
diff --git a/pydhcplib/scripts/pydhcp.py b/pydhcplib/scripts/pydhcp.py
new file mode 100644 (file)
index 0000000..8cd8a47
--- /dev/null
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+
+
+import dhcp_constants
+import dhcp_packet
+import dhcp_network
+
+import type_hw_addr
+import type_ipv4
+import type_strlist
+
+import sys
+
+from optparse import OptionParser
+
+
+parser = OptionParser()
+
+""" Action options """
+parser.add_option("-L", "--listen", action="store_true",dest="listen", help="",default="False")
+parser.add_option("-E", "--emit",  action="store_true",dest="emit", help="",  default="False")
+parser.add_option("-R", "--readable-conversion",  action="store_true",dest="readable", help="", default="False")
+parser.add_option("-B", "--binary-conversion",  action="store_true",dest="binary", help="", default="False")
+
+
+parser.add_option("-s", "--source-file", action="store",dest="source", help="", default="False", type="string")
+parser.add_option("-d", "--destination-file", action="store",dest="destination", help="", default="False", type="string")
+
+
+
+(options, args) = parser.parse_args()
+
+print options
+
+
+
+def main() :
+    ActionSum = 0
+    for Action in (options.listen,options.emit,options.readable,options.binary) :
+        if Action == True : ActionSum += 1
+    if ActionSum > 1 :
+        print "Command line error : [-L -E -R -B] Only one of these actions can be taken."
+        sys.exit(0)
+
+    if options.readable == True : r_conversion()
+
+
+
+def listen(port) :
+    pass
+
+def emit(address,port) :
+    pass
+
+def r_conversion() :
+    rawdata = rawdata = sys.stdin.read()
+    while ( len(rawdata)>0 ) :
+        readdata = dhcp_packet.DhcpPacket()
+        readdata.DecodePacket(rawdata)
+        readdata.PrintHeaders()
+        readdata.PrintOptions()
+        rawdata = rawdata = sys.stdin.read()
+
+def b_conversion() :
+    pass
+
+main()
diff --git a/pydhcplib/setup.py b/pydhcplib/setup.py
new file mode 100755 (executable)
index 0000000..2c8b955
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+
+from distutils.core import setup
+
+fr8_manpages=['man/fr/man8/pydhcp.8.gz']
+fr3_manpages=['man/fr/man3/pydhcplib.3.gz',
+              'man/fr/man3/pydhcplib.DhcpBasicPacket.3.gz',
+              'man/fr/man3/pydhcplib.DhcpPacket.3.gz',
+              'man/fr/man3/pydhcplib.hwmac.3.gz',
+              'man/fr/man3/pydhcplib.ipv4.3.gz',
+              'man/fr/man3/pydhcplib.strlist.3.gz']
+en8_manpages=['man/man8/pydhcp.8.gz']
+
+setup(name='pydhcplib',
+      version="0.1",
+      license='GPL v2',
+      description='Dhcp client/server library',
+      author='Mathieu Ignacio',
+      author_email='tamtam@anemon.org',
+      url='http://pydhcplib.tuxfamily.org/',
+      packages=['pydhcplib'],
+      scripts=['scripts/pydhcp'],
+      data_files=[("share/man/man8",en8_manpages),
+                  ("share/man/fr/man8",fr8_manpages),
+                  ("share/man/fr/man3",fr3_manpages)
+                  ])