Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial Import of Tools

  • Loading branch information...
commit 0ff60b72cc77da6d30f6562233f81d83cd5c11f6 1 parent d08b88d
Jim Cortez authored
Showing with 326 additions and 0 deletions.
  1. +120 −0 ConnectionUtils.py
  2. +79 −0 Discovery.py
  3. +122 −0 client.py
  4. +5 −0 config.example
View
120 ConnectionUtils.py
@@ -0,0 +1,120 @@
+import sys, os, threading, uuid, readline, errno
+import ssl, socket, select, asyncore, asynchat
+import hmac, hashlib, time
+from urllib import urlencode
+
+class async_chat_ssl(asynchat.async_chat):
+ """ Asynchronous connection with SSL support. """
+
+ def __init__(self, addr):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.ssl = ssl.wrap_socket(sock)
+
+ #print "connecting to ", addr
+ self.ssl.connect(addr)
+
+ asynchat.async_chat.__init__(self, sock=self.ssl)
+
+ self.send = self._ssl_send
+ self.recv = self._ssl_recv
+
+ def _ssl_send(self, data):
+ """ Replacement for self.send() during SSL connections. """
+ try:
+ result = self.write(data)
+ return result
+ except ssl.SSLError, why:
+ if why[0] in (asyncore.EWOULDBLOCK, errno.ESRCH):
+ return 0
+ else:
+ raise ssl.SSLError, why
+ return 0
+
+ def _ssl_recv(self, buffer_size):
+ """ Replacement for self.recv() during SSL connections. """
+ try:
+ data = self.read(buffer_size)
+ if not data:
+ self.handle_close()
+ return ''
+ return data
+ except ssl.SSLError, why:
+ if why[0] in (asyncore.ECONNRESET, asyncore.ENOTCONN,
+ asyncore.ESHUTDOWN):
+ self.handle_close()
+ return ''
+ elif why[0] == errno.ENOENT:
+ # Required in order to keep it non-blocking
+ return ''
+ else:
+ raise
+
+class ConnectionHandler(async_chat_ssl):
+ def __init__(self, addr, queue=None, onMessageRecieved=None, onMessageRecievedContext=None):
+ async_chat_ssl.__init__(self, addr)
+ self.ibuffer = []
+ self.set_terminator("|END")
+ self.onMessageReceived = onMessageRecieved
+ self.onMessageRecievedContext = onMessageRecievedContext
+ self.queue = queue
+ #print("Handler initialized")
+
+ def returnMessage(self, msg):
+ if self.queue != None:
+ self.queue.put(msg)
+ else:
+ method = self.onMessageReceived
+ if self.onMessageRecievedContext != None:
+ context = self.onMessageRecievedContext
+ context.method(msg)
+ else:
+ method(msg)
+
+ def collect_incoming_data(self, data):
+ self.ibuffer.append(data)
+
+ def found_terminator(self):
+ response = "".join(self.ibuffer)
+ self.ibuffer = []
+ self.returnMessage(response)
+
+def createSession(connection, app_id, consumer_key, secret, app_name):
+ keyopts = {
+ 'app_id': app_id,
+ 'consumer_key': consumer_key,
+ 'secret': hmac.new(secret, consumer_key, hashlib.sha1).hexdigest()
+ }
+
+ createCommand = "SESSION|CREATE|%s|%s|END" %(urlencode(keyopts), app_name)
+ print "SENDING:", createCommand
+ connection.handler.push(createCommand)
+
+def resetSession(connection, instanceID):
+ resetCommand = "SESSION|RESET|%s|END" % instanceID
+ print "SENDING:", resetCommand
+ connection.handler.push(resetCommand)
+
+def authSession(connection, code):
+ cert = str(ssl.DER_cert_to_PEM_cert(connection.handler.socket.getpeercert(True))).rstrip()
+
+ #Some machines already have the \n.. I can't even...wha??
+ if '\n-----END CERTIFICATE-----' not in cert:
+ cert = cert.replace('-----END CERTIFICATE-----', '\n-----END CERTIFICATE-----')
+
+ signature = hmac.new(code, cert, hashlib.sha1).hexdigest()
+ authCommand = "SESSION|AUTH|%s|END" % signature
+
+ print "SENDING:", authCommand
+ connection.handler.push(authCommand)
+
+class Connection():
+ def __init__(self, host, port, queue=None, onMessageRecieved=None, onMessageRecievedContext=None):
+ self.buffer = []
+ self.handler = ConnectionHandler((host, port), queue, onMessageRecieved, onMessageRecievedContext)
+
+ def startLoop(self):
+ asyncore.loop(1)
+ print "Connection Closed"
+
+ def close(self):
+ self.handler.close_when_done()
View
79 Discovery.py
@@ -0,0 +1,79 @@
+import select, time, socket
+
+PYBONJOUR_AVAILABLE = True
+try:
+ import pybonjour
+except:
+ print("pybonjour not installed, no auto discovery available")
+ PYBONJOUR_AVAILABLE = False
+
+regtype = "_yctvwidgets._tcp"
+timeout = 5
+resolved_addrs = []
+
+def resolve_callback(sdRef, flags, interfaceIndex, errorCode, fullname, hosttarget, port, txtRecord):
+ if errorCode == pybonjour.kDNSServiceErr_NoError:
+ global resolved_addrs
+ ip = socket.gethostbyname(hosttarget)
+ if ip not in [entry.get("host") for entry in resolved_addrs]:
+ print("resolved %s:%d" % (ip, port))
+ resolved_addrs.append({"sdRef":sdRef, "hostname": hosttarget, "host": ip, "port": port})
+
+def browse_callback(sdRef, flags, interfaceIndex, errorCode, serviceName,
+ regtype, replyDomain):
+ if errorCode != pybonjour.kDNSServiceErr_NoError:
+ return
+
+ if not (flags & pybonjour.kDNSServiceFlagsAdd):
+ print 'Service removed'
+ return
+
+ print 'DC Service found; resolving'
+ global resolved_addrs
+ resolve_sdRef = pybonjour.DNSServiceResolve(0,
+ interfaceIndex,
+ serviceName,
+ regtype,
+ replyDomain,
+ resolve_callback)
+
+ try:
+ while True:
+ ready = select.select([resolve_sdRef], [], [], timeout)
+ if resolve_sdRef not in ready[0]:
+ break
+ pybonjour.DNSServiceProcessResult(resolve_sdRef)
+ finally:
+ resolve_sdRef.close()
+
+def discover(timeout=10):
+ timeout = float(timeout)
+ browse_sdRef = pybonjour.DNSServiceBrowse(regtype = regtype, callBack = browse_callback)
+ cTime = time.time()
+ try:
+ try:
+ while len(resolved_addrs) == 0 and time.time()-cTime < timeout:
+ ready = select.select([browse_sdRef], [], [], timeout/2)
+ if browse_sdRef in ready[0]:
+ pybonjour.DNSServiceProcessResult(browse_sdRef)
+ except KeyboardInterrupt:
+ pass
+ finally:
+ browse_sdRef.close()
+
+ if len(resolved_addrs) > 0:
+ if len(resolved_addrs) == 1:
+ print("Found 1 matching Service, connecting to %s:%d" % (resolved_addrs[0].get("host"), resolved_addrs[0].get("port")))
+ return resolved_addrs[0].get("host"), resolved_addrs[0].get("port")
+ else:
+ print("Found Services:")
+ print("#\tHost:Port")
+ for i in range(0, len(resolved_addrs)):
+ print("%d:\t%s:%d" % (i, resolved_addrs[i].get("host"), resolved_addrs[i].get("port")))
+ user_option = input("Please choose service # to connect:")
+ return resolved_addrs[user_option].get("host"), resolved_addrs[user_option].get("port")
+ else:
+ return None, None
+
+if __name__ == "__main__":
+ print discover()
View
122 client.py
@@ -0,0 +1,122 @@
+#!/usr/bin/python
+import sys, ConfigParser
+try:
+ import argparse
+except:
+ print("'argparse' python library not found.\n If using Ubuntu, run 'sudo apt-get install python-argparse'")
+ sys.exit()
+from Discovery import *
+from ConnectionUtils import *
+
+DEFAULT_APP_ID = "0xeTgF3c"
+DEFAULT_CONSUMER_KEY = "dj0yJmk9T1Y0MmVIWWEzWVc3JmQ9WVdrOU1IaGxWR2RHTTJNbWNHbzlNVEUzTkRFM09ERTJNZy0tJnM9Y29uc3VtZXJzZWNyZXQmeD0yNA--"
+DEFAULT_SECRET = "1b8f0feb4d8d468676293caa769e19958bf36843"
+DEFAULT_APP_NAME = "Test Client (client.py)"
+
+IS_RUNNING=False
+
+class ReceiverThread(threading.Thread):
+ def __init__(self, connection):
+ threading.Thread.__init__(self)
+ self.connection=connection
+ self.isClosing=False
+
+ def run(self):
+ while not self.isClosing:
+ cmnd = getUserInput("SEND: ")
+ if cmnd == "" or cmnd == -1 : continue
+ if cmnd == "q": break
+
+ self.connection.handler.push(cmnd)
+ self.connection.handler.close_when_done()
+
+userInputState = {"prompt":"", "isWaiting":False}
+def printMessage(msg):
+ if(userInputState["isWaiting"]):
+ print "\n",msg
+ sys.stdout.write(userInputState["prompt"])
+ else:
+ print msg
+
+def getUserInput(prompt):
+ userInputState["isWaiting"] = True
+ userInputState["prompt"] = prompt
+ data = raw_input(prompt)
+ userInputState["isWaiting"] = False
+ return data
+
+def api(args):
+ def onMessageRecieved(msg):
+ print "RCVD:", msg
+
+ client = Connection(args.host, args.port, onMessageRecieved=onMessageRecieved)
+
+ if args.instanceId:
+ resetSession(client, args.instanceId)
+ elif args.manual_auth == False:
+ createSession(client, args.app_id, args.consumer_key, args.secret, args.app_name)
+
+ authSession(client, raw_input("Please enter code:"))
+
+ inputReader = ReceiverThread(client)
+ inputReader.start()
+
+ client.startLoop();
+ inputReader.isClosing=True
+
+def setupReadlineHistory(historyFile):
+ try:
+ readline.read_history_file(historyFile)
+ readline.parse_and_bind("set set editing-mode vi")
+ readline.parse_and_bind("set horizontal-scroll-mode On")
+ except IOError, e:
+ print(e)
+ pass
+ import atexit
+ atexit.register(readline.write_history_file, historyFile)
+
+def parse_args():
+ parser = argparse.ArgumentParser(description='Connect to a Device Communication-enabled TV and send messages')
+ parser.add_argument('host', nargs='?', help='hostname or IP to connect to, omit for automatic search')
+ parser.add_argument('port', type=int, nargs='?', default=8099, help='port of device, defaults to 8099')
+ parser.add_argument('-m', '--manual-auth', action='store_true', help='do not prompt for code, just connect')
+ parser.add_argument('-i', '--instanceId', help='use an instanceID to connect, will override --manual-auth')
+ parser.add_argument('-y', '--history', default=os.path.join(os.environ["HOME"], ".client.py.hist"), help='use non-default history file')
+ parser.add_argument('-c', '--config', default=os.path.join(os.environ["HOME"], ".client.py.config"), help='configuration file that stores authorization keys, leave blank to use default non-production keys. See config.sample for configuration file example. Default location: %s' % os.path.join(os.environ["HOME"], ".client.py.config"))
+ return parser.parse_args()
+
+def load_config(args):
+ config = ConfigParser.RawConfigParser({"app_id": DEFAULT_APP_ID, "consumer_key":DEFAULT_CONSUMER_KEY, "secret": DEFAULT_SECRET, "app_name":DEFAULT_APP_NAME})
+ configsRead = config.read(args.config)
+
+ if configsRead is None:
+ print("WARNING: Using default auth keys. Note these can only be used in a simulator environment. See --help for more information." % args.config)
+ elif args.config not in configsRead:
+ print("Unable to load config file %s, using default auth keys. Note these can only be used in a simulator environment." % args.config)
+
+ args.app_id = config.get("DEFAULT", "app_id")
+ args.consumer_key = config.get("DEFAULT", "consumer_key")
+ args.secret = config.get("DEFAULT", "secret")
+ args.app_name = config.get("DEFAULT", "app_name")
+
+
+
+def main():
+ args = parse_args()
+ load_config(args)
+
+ if not args.host and PYBONJOUR_AVAILABLE:
+ print("Starting automatic discovery... For manual usage, see the -h option")
+ args.host, args.port = discover()
+ if args.host is None or args.port is None:
+ print("Unable to automatically resolve host and port. For manual usage, see the -h option")
+ elif not args.host and not PYBONJOUR_AVAILABLE:
+ print("Automatic search not available, please install pybonjour")
+
+ if args.host != None and args.port != None:
+ setupReadlineHistory(args.history)
+
+ api(args)
+
+if __name__ == "__main__":
+ main()
View
5 config.example
@@ -0,0 +1,5 @@
+[DEFAULT]
+secret = <your_secret>
+consumer_key = <your_consumer_key>
+app_id = <your_app_id>
+app_name = My Test Client
Please sign in to comment.
Something went wrong with that request. Please try again.