#!/usr/bin/env python # # Wake On Lan - Magic Packet 0.1 # Copyright (C) 2009 Damian Pasternok # This code is free software under the GPLv3+. # For more information see . # # Used some parts of code of: # Brian McErlean # http://mail.python.org/pipermail/python-list/2002-August/159592.html # Marc Balmer # http://gsd.di.uminho.pt/jpo/software/wakeonlan/mini-howto/wolpython.txt ### config use_remote_tld_list = "no" # if set to "no" tld_local will be used tld_remote = "http://data.iana.org/TLD/tlds-alpha-by-domain.txt" ### do not change tld_local unless you know what you are doing # tld_local (Version 2009072901, Last Updated Thu Jul 30 07:07:01 2009 UTC) tld_local = r"""ac|ad|ae|aero|af|ag|ai|al|am|an|ao|aq|ar|arpa|as|asia|at| au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|biz|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz| ca|cat|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|com|coop|cr|cu|cv|cx|cy|cz|de|dj| dk|dm|do|dz|ec|edu|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg| gh|gi|gl|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im| in|info|int|io|iq|ir|is|it|je|jm|jo|jobs|jp|ke|kg|kh|ki|km|kn|kp|kr|kw| ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mil|mk|ml|mm|mn| mo|mobi|mp|mq|mr|ms|mt|mu|museum|mv|mw|mx|my|mz|na|name|nc|ne|net|nf|ng| ni|nl|no|np|nr|nu|nz|om|org|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|pro|ps|pt|pw| py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su| sv|sy|sz|tc|td|tel|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|travel|tt|tv|tw|tz| ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|xn--0zwm56d|xn--11b5bs3a9aj6g| xn--80akhbyknj4f|xn--9t4b11yi5a|xn--deba0ad|xn--g6w251d|xn--hgbk6aj7f53bba| xn--hlcj6aya9esc7a|xn--jxalpdlp|xn--kgbechtv|xn--zckzah|ye|yt|yu|za|zm| zw""" ### code __author__="Damian Pasternok " __program__="Wake On Lan - Magic Packet" __version__="0.1" __date__ ="$2009-07-01 16:57:06$" import pygtk pygtk.require('2.0') import gtk import re import struct import socket import sys import os import urllib tld_local = tld_local.replace("\n", "") class TLDParser: def __init__(self, tld): self.url = urllib.urlopen(tld) self.html = self.url.readlines() # no version header (probably 404 or something) if not re.match("^#.*$", self.html[0]): self.err = 1 else: self.err = 0 def get_header(self): self.header = self.html[0] return self.header def parse_tlds(self): self.tlds = "" self.tlds = self.html self.tlds.pop(0) # remove header i = 0 for domain in self.tlds: self.tlds[i] = domain.lower() self.tlds[i] = self.tlds[i].replace("\n", "|") i += 1 # remove "|" from last item self.tlds[len(self.tlds)-1] = self.tlds[len(self.tlds)-1].replace("|", "") return self.tlds # args: type # regexp - used by 'use_remote_tld_list = "yes"' # variable - used by '--update-tlds' (create new tld_local variable) def get_tld(self, type): self.tld_local = "" self.tld_local_list = [] # or list() if type == "regexp": for domain in self.parse_tlds(): self.tld_local += domain self.tlds = self.tlds[:-1] # remove last "|" return self.tld_local elif type == "variable": self.tld_local_list.append(self.get_header()) self.tld_local_list[0] = self.tld_local_list[0].replace("# ", "# tld_local (") self.tld_local_list[0] = self.tld_local_list[0].replace("\n", ")\n") self.tld_local_list.append("tld_local = r\"\"\"") # create fancy variable string fitted to vt100 based terminals i = 1 for domain in self.parse_tlds(): if len(self.tld_local_list[i]) > 70: self.tld_local_list[i] += "\n" self.tld_local_list.append("") i += 1 self.tld_local_list[i] += domain continue else: self.tld_local_list[i] += domain self.tld_local_list[i] += "\"\"\"" for lines in self.tld_local_list: self.tld_local += lines return self.tld_local class Group: def __init__(self, l, size): self.size = size self.l = l def __getitem__(self, group): idx = group * self.size if idx > len(self.l): raise IndexError("Out of range") return self.l[idx:idx+self.size] class MagicPacket: # calculate broadcast def get_broadcast(self, inetaddr, submask): try: self.inetaddr = socket.gethostbyname(inetaddr) except: self.err = 1 return -1 self.inetaddr = self.inetaddr.split('.') self.submask = submask.split('.') self.broadcast = "" self.broadcast_list = [] i = 0 for sbyte in self.submask: self.submask[i] = int(sbyte) ^ 0xff # xor the binary subnet mask self.broadcast_list.append(int(self.inetaddr[i]) | self.submask[i]) self.broadcast += str(self.broadcast_list[i]) self.broadcast += "." i += 1 self.broadcast = self.broadcast[:-1] return self.broadcast def mac_into_list(self, macaddr): # check MAC format self.macaddr = macaddr.split(':') if len(self.macaddr) != 6: self.macaddr = macaddr.split('-') if len(self.macaddr) != 6: self.macaddr = [] # group MAC bytes into list for pair in Group(macaddr, 2): self.macaddr.append(pair) self.macaddr.pop() # remove last empty item from list i = 0 for pair in self.macaddr: # all letters in uppercase self.macaddr[i] = pair.upper() i += 1 return self.macaddr def send_magic_packet(self, macaddr, inetaddr, submask, remport): self.macaddr = self.mac_into_list(macaddr) # prepare magic packet self.hw_addr = struct.pack('BBBBBB', int(self.macaddr[0], 16), int(self.macaddr[1], 16), int(self.macaddr[2], 16), int(self.macaddr[3], 16), int(self.macaddr[4], 16), int(self.macaddr[5], 16)) self.msg = '\xff' * 6 + self.hw_addr * 16 self.s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) self.err = 0 # set to 1 if failed to resolve hostname if inetaddr: self.broadcast = self.get_broadcast(inetaddr, submask) if not self.err: self.s.sendto(self.msg, (self.broadcast, int(remport))) else: self.broadcast = "255.255.255.255" self.s.sendto(self.msg, (self.broadcast, int(remport))) self.s.close() return self.broadcast # returns broadcast address class Errors: errors = "" def show_errors(self): dialog = gtk.MessageDialog(self.parent_window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Unable to send Magic Packet due to following errors: %s" % self.errors) dialog.set_title(__program__) dialog.run() dialog.destroy() def combine_errors(self, error_message): self.errors += error_message def check_mac(self, macaddr): self.res = re.match(r"^[\dA-Fa-f]{2}(-[\dA-Fa-f]{2}){5}$|" "^[\dA-Fa-f]{2}(:[\dA-Fa-f]{2}){5}$|" "^[\dA-Fa-f]{2}([\dA-Fa-f]{2}){5}$", macaddr) if self.res: return 0 else: self.combine_errors("\n- wrong MAC - correct MAC formats are " "(case-insensitive):\n 00-74-F1-37-4B-59, " "00:74:F1:37:4B:59 or 0074F1374B59") def check_inetaddr(self, inetaddr): if use_remote_tld_list == "yes": try: parser = TLDParser(tld_remote) tld = parser.get_tld("regexp") if parser.err: # wrong tld url tld = tld_local IndexErrors() except: dialog = gtk.MessageDialog(self.parent_window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_WARNING, gtk.BUTTONS_OK, "Unable to get the list of Top-level Domains.\n" "Using Top-level Domains from tld_local.") dialog.set_title(__program__) dialog.run() dialog.destroy() tld = tld_local elif use_remote_tld_list == "no": tld = tld_local self.prog = re.compile(r"^((\d{1,2}|1\d{2}|2[0-4]\d{1}|25[0-5])" # ip "(\.(\d{1,2}|1\d{2}|2[0-4]\d{1}|25[0-5])){3})|" "([A-Za-z\d]+((\-|_){0,1}[A-Za-z\d])*" # fqdn "(\.[A-Za-z\d]+((\-|_){0,1}[A-Za-z\d])*)*" "\.(%s))$" % tld) self.res = self.prog.match(inetaddr) if self.res: return 0 else: self.combine_errors("\n- wrong internet address - correct formats " "are:\n IP address (e.g. 192.168.0.1) or " "fully qualified domain\n name (e.g. " "bgp-0-2.br0.nyc1.isp.net)") def check_submask(self, submask): self.res = re.match(r"^(\d{1,2}|1\d{2}|2[0-4]\d{1}|25[0-5])" "(\.(\d{1,2}|1\d{2}|2[0-4]\d{1}|25[0-5])){3}$", submask) if self.res: return 0 else: self.combine_errors("\n- wrong subnet mask - correct format is e.g." " 255.255.255.0") def check_remport(self, remport): self.res = re.match(r"^(\d{1,4}|[1-5]\d{4}|6([0-4]\d{3}|" "5[0-4]\d{2}|55[0-2]\d{1}|553[0-5]))$", remport) if self.res: return 0 else: self.combine_errors("\n- wrong remote port - use one from range " "0-65535\n (typically 0, 7 or 9)") def __init__(self, window): self.parent_window = window class CommandLine: def version(self): version_message = r"""%s %s Copyright (C) 2009 %s %s comes with ABSOLUTELY NO WARRANTY. You may redistribute copies of %s under the terms of the GNU General Public License version 3 or later. For more information see .""" % ( __program__, __version__, __author__, __program__, __program__) return version_message def help(self): help_message = r"""Usage: %s [OPTION]... Available options: --update-tlds try to download list of Top-level Domains from URL defined in tld_remote variable and update tld_local variable in script --help display this help and exit --version output version information and exit """ % sys.argv[0] return help_message class MainWindow: def destroy(self, widget, data=None): gtk.main_quit() def wake_me(self, button, data=None): self.check = Errors(self.window) self.check.check_mac(self.edit_macaddr.get_text()) if self.select_target.get_active_text() == "Internet": self.check.check_inetaddr(self.edit_inetaddr.get_text()) self.check.check_submask(self.edit_submask.get_text()) self.check.check_remport(self.edit_remport.get_text()) if len(self.check.errors): self.check.show_errors() else: self.send = MagicPacket() broadcast = self.send.send_magic_packet( self.edit_macaddr.get_text(), self.edit_inetaddr.get_text(), self.edit_submask.get_text(), self.edit_remport.get_text()) if self.send.err: self.statusbar.push(0, "Unable to send Magic Packet. Can't resolve hostname...") return -1 fancy_mac = "" for mbyte in self.send.mac_into_list(self.edit_macaddr.get_text()): fancy_mac += mbyte fancy_mac += ":" fancy_mac = fancy_mac[:-1] # remove last ":" self.statusbar.push(0, "Magic Packet sent to %s (%s)..." % (broadcast, fancy_mac)) def show_about(self, widget, string): dialog = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, string) dialog.set_title(__program__) dialog.run() dialog.destroy() def select_send_option(self, button, data=None): if self.select_target.get_active_text() == "Internet": self.edit_inetaddr.set_sensitive(True) self.edit_submask.set_sensitive(True) elif self.select_target.get_active_text() == "Local Subnet": self.edit_inetaddr.set_text("") self.edit_submask.set_text("") self.edit_inetaddr.set_sensitive(False) self.edit_submask.set_sensitive(False) def __init__(self): # main window self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) self.window.connect("destroy", self.destroy) self.window.set_title(__program__) self.window.set_position(gtk.WIN_POS_CENTER) # root vbox self.root_vbox = gtk.VBox(False, 0) # menu self.menu = gtk.Menu() self.about_item = gtk.MenuItem("_About...") self.menu.append(self.about_item) self.cmd = CommandLine() self.about_item.connect("activate", self.show_about, self.cmd.version()) self.root_menu = gtk.MenuItem("_Help") self.root_menu.set_right_justified(True) self.root_menu.set_submenu(self.menu) self.menu_bar = gtk.MenuBar() self.menu_bar.append(self.root_menu) self.root_vbox.pack_start(self.menu_bar, False, False, 0) # frame self.frame = gtk.Frame(" %s" % __program__) self.window.add(self.root_vbox) self.root_vbox.pack_start(self.frame, False, False, 0) # statusbar self.statusbar = gtk.Statusbar() self.root_vbox.pack_end(self.statusbar, False, False, 0) # labels self.label1 = gtk.Label("Mac Address") self.label1.set_alignment(1, 0.5) self.label2 = gtk.Label("Internet Address") self.label2.set_alignment(1, 0.5) self.label3 = gtk.Label("Subnet Mask") self.label3.set_alignment(1, 0.5) self.label4 = gtk.Label("Send options") self.label4.set_alignment(1, 0.5) self.label5 = gtk.Label("Remote Port Number") self.label5.set_alignment(1, 0.5) # edit boxes & selection self.edit_macaddr = gtk.Entry() self.edit_inetaddr = gtk.Entry() self.edit_submask = gtk.Entry() self.select_target = gtk.combo_box_new_text() self.select_target.append_text("Local Subnet") self.select_target.append_text("Internet") self.select_target.set_active(0) self.edit_remport = gtk.Entry() # combobox action self.select_target.connect("changed", self.select_send_option) # hide by default "internet address" and "subnet mask" # (they are not required when send local magic packet) self.edit_inetaddr.set_sensitive(False) self.edit_submask.set_sensitive(False) # default values self.edit_macaddr.set_text("00-74-F1-37-4B-59") self.edit_remport.set_text("9") # table self.table = gtk.Table(5, 2) self.table.set_row_spacings(4) self.table.set_col_spacings(10) self.table.attach(self.label1, 0, 1, 0, 1) self.table.attach(self.label2, 0, 1, 1, 2) self.table.attach(self.label3, 0, 1, 2, 3) self.table.attach(self.label4, 0, 1, 3, 4) self.table.attach(self.label5, 0, 1, 4, 5) self.table.attach(self.edit_macaddr, 1, 2, 0, 1) self.table.attach(self.edit_inetaddr, 1, 2, 1, 2) self.table.attach(self.edit_submask, 1, 2, 2, 3) self.table.attach(self.select_target, 1, 2, 3, 4) self.table.attach(self.edit_remport, 1, 2, 4, 5) # vertical box self.vbox = gtk.VBox(False, 8) self.vbox.set_border_width(16) self.frame.add(self.vbox) # add table to vbox self.vbox.pack_start(self.table, False, False, 0) # add separator to vbox self.vbox.pack_start(gtk.HSeparator(), False, False, 0) # wake button self.button = gtk.Button("Wake Me Up") self.button.connect("clicked", self.wake_me) self.vbox.pack_end(self.button, False, False, 0) self.window.show_all() def main(): cmd = CommandLine() if len(sys.argv) == 1: MainWindow() gtk.main() elif len(sys.argv) == 2: if sys.argv[1] == "--update-tlds": try: parser = TLDParser(tld_remote) if parser.err: # wrong tld url IndexErrors() new_tld_local = parser.get_tld("variable") except: print "Unable to get the list of Top-level Domains.\nExiting." return -1 f = open(sys.argv[0], "r") script = f.read() f.close() script = re.sub("# tld_local(.|\n)*[\w]{2}\"\"\"", new_tld_local, script, re.M) os.remove(sys.argv[0]) f = open(sys.argv[0], "w") os.chmod(sys.argv[0], 0755) f.write(script) f.close() print "Top-level Domains updated successfully." while 1: print "Do you want to run the main program? [n]", key = raw_input() if key == "y" or key == "Y": MainWindow() gtk.main() break elif key == "n" or key == "N" or key == "": break elif sys.argv[1] == "--version": print cmd.version() else: print cmd.help() else: print cmd.help() return 0 if __name__ == "__main__": main()