From: Quentin Smith Date: Thu, 11 Oct 2007 07:56:09 +0000 (-0400) Subject: Initial checkin of a DHCP server (untested) X-Git-Tag: sipb-xen-dhcp/1~11 X-Git-Url: http://xvm.mit.edu/gitweb/invirt/packages/invirt-dhcp.git/commitdiff_plain/377cd6bb3cdbf4b23e7cf6b8ca5d0a9f21fb36bb Initial checkin of a DHCP server (untested) svn path=/trunk/dhcp/; revision=189 --- 377cd6bb3cdbf4b23e7cf6b8ca5d0a9f21fb36bb diff --git a/anemon-svn.tar.gz b/anemon-svn.tar.gz new file mode 100644 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 index 0000000..5c7721c --- /dev/null +++ b/dhcpserver.py @@ -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 index 0000000..fa59be0 --- /dev/null +++ b/event_logger.py @@ -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 index 0000000..4a4f44a --- /dev/null +++ b/pydhcplib/COPYING @@ -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. + + 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.) + +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. + + 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. + + 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 index 0000000..04eadbb --- /dev/null +++ b/pydhcplib/README @@ -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 index 0000000..c8f3887 --- /dev/null +++ b/pydhcplib/examples/client_example.py @@ -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 index 0000000..c6e7550 --- /dev/null +++ b/pydhcplib/examples/gen_packet_example.py @@ -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 index 0000000..9c5de80 --- /dev/null +++ b/pydhcplib/examples/hwaddr_example.py @@ -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 index 0000000..2a74c67 --- /dev/null +++ b/pydhcplib/examples/ipv4_example.py @@ -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 index 0000000..1a079ab --- /dev/null +++ b/pydhcplib/examples/server_example.py @@ -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 index 0000000..9a597ad --- /dev/null +++ b/pydhcplib/examples/strlist_example.py @@ -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 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 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 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 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 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 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 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 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 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 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 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 index 0000000..b650ceb --- /dev/null +++ b/pydhcplib/pydhcplib/__init__.py @@ -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 index 0000000..26a7254 --- /dev/null +++ b/pydhcplib/pydhcplib/dhcp_basic_packet.py @@ -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 index 0000000..295aa78 --- /dev/null +++ b/pydhcplib/pydhcplib/dhcp_constants.py @@ -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 index 0000000..5e95707 --- /dev/null +++ b/pydhcplib/pydhcplib/dhcp_network.py @@ -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 index 0000000..4f564f7 --- /dev/null +++ b/pydhcplib/pydhcplib/dhcp_packet.py @@ -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 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 index 0000000..f42dec0 --- /dev/null +++ b/pydhcplib/pydhcplib/type_ipv4.py @@ -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 index 0000000..ca5f1c0 --- /dev/null +++ b/pydhcplib/pydhcplib/type_strlist.py @@ -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 index 0000000..a0fec1b --- /dev/null +++ b/pydhcplib/scripts/pydhcp @@ -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 index 0000000..8cd8a47 --- /dev/null +++ b/pydhcplib/scripts/pydhcp.py @@ -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 index 0000000..2c8b955 --- /dev/null +++ b/pydhcplib/setup.py @@ -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) + ])