Repository: seiferteric/qrtun Branch: master Commit: 9328a0e83f43 Files: 4 Total size: 8.9 KB Directory structure: gitextract_wmobx1mm/ ├── .gitignore ├── LICENSE ├── README.md └── qrtun_async.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.png *.bmp *.jpg *.swp ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2016 Eric Seifert Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # qrtun IP over qrcode This implements a tun interface to send biderectional data using qr codes displayed on your monitor and read using your webcam. Set up two linux PCs with a monitor and webcam facing eachother (a bit finicky to get aligned) and start the program: On the first computer: ``` sudo python qrtun_async.py 1 ``` On the second: ``` sudo python qrtun_async.py 2 ``` This will create a qrtun[1|2] interface with 10.0.8.[1|2] on each computer respectively. It will then start reading packets from that interface and show a qrcode in a window for the other to read. The data is base32 encoded due to limations in what characters you can encode in a alphanumeric qr code. Was going to use a tap device but had problems when packets were fragmented with pytun library, but just switched to a tun device and works now! You can use the up and down arrows to increase/decrease the size of the qr code. You can also press the space bar to toggle camera view mode to show what your camera sees, to help with alignment. I have only tested on Ubuntu 16.04 and needed these packages: ``` apt-get install qrencode git libzbar-dev python-pygame zbar-tools pip install python-pytun zbar pillow pip install git+https://github.com/primetang/qrtools.git ``` Check out these vids! * [Without seq/ack](https://www.youtube.com/watch?v=_BUlrzEvwEE) * [First Test](https://www.youtube.com/watch?v=E4qs1FmtDUA) * [Second](https://www.youtube.com/watch?v=kc9COP5dALU) * [SSH!](https://www.youtube.com/watch?v=N_Qr5AP_2wU) [Blog Post](http://seiferteric.com/?p=356) ================================================ FILE: qrtun_async.py ================================================ from pytun import TunTapDevice, IFF_TAP, IFF_TUN, IFF_NO_PI import sys import select import signal import os import cv2 import scipy.misc import StringIO import pygame import subprocess import zbar from base64 import b32encode, b32decode SIZE = 1024 class QRTun(object): def __init__(self, side): self.side = int(side) if self.side not in [1,2]: print("Side must be 1 or 2") raise Exception("Invalid Side") self.tun = TunTapDevice(flags=IFF_TUN|IFF_NO_PI, name='qrtun%d'%self.side) self.tun.addr = '10.0.8.%d'%(self.side) if self.side == 1: self.other_side = 2 else: self.other_side = 1 self.tun.netmask = '255.255.255.0' #MTU must be set low enough to fit in a single qrcode self.tun.mtu = 300 self.epoll = select.epoll() self.epoll.register(self.tun.fileno(), select.EPOLLIN) self.tun.up() #self.outfile = 'resources/toscreen%d.png'%(self.side) self.outfile = None self.infile = 'resources/toscreen%d.png'%(self.other_side) self.indata = None self.olddata = "" self.outdata = "" self.running = False self.vc = cv2.VideoCapture(0) self.vc.set(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT, 720) self.vc.set(cv2.cv.CV_CAP_PROP_FRAME_WIDTH, 1280) self.scanner = zbar.ImageScanner() self.scanner.parse_config('enable') pygame.init() pygame.event.set_allowed(None) pygame.event.set_allowed([pygame.KEYDOWN, pygame.QUIT]) self.screen = pygame.display.set_mode((SIZE, SIZE)) self.scale = 12 self.display_cam = False pygame.display.set_caption("qrtun - QR Code scale %d"%(self.scale)) def read_tun(self): events = self.epoll.poll(0) if events: self.outdata = self.tun.read(self.tun.mtu) return True return False def write_qrcode(self): p = subprocess.Popen(['qrencode', '-o', '-', '-s', str(self.scale)], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) std_out, std_err = p.communicate(input=b32encode(self.outdata).replace('=', '/')) if std_err: raise Exception("qrencodeError", std_err.strip()) self.outfile = StringIO.StringIO(std_out) if self.display_cam: cimg = StringIO.StringIO() self.inframe.save(cimg, 'png') cimg.seek(0) cpimg = pygame.image.load(cimg) self.screen.blit(cpimg, (0,0)) pygame.display.flip() else: if self.outfile and not self.outfile.closed: pimg = pygame.image.load(self.outfile) if pimg.get_width() > self.screen.get_width() or pimg.get_height() > self.screen.get_height(): pygame.display.set_mode((pimg.get_width(), pimg.get_height())) self.screen.fill((0,0,0)) self.screen.blit(pimg, (0,0)) pygame.display.flip() self.msg_read = False def write_tun(self): try: if len(self.indata.get('body')) > 0: data = self.indata.get('body') if data != self.olddata: self.tun.write(data) #This is a hacky way to avoid dup packets... #surely a better way to do this... self.olddata = data except: print("Failed to write to tun!") def read_qrcode(self): p = subprocess.Popen(['zbarimg', '-q', '--raw', 'PNG:-'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) temp_png = StringIO.StringIO() self.inframe.save(temp_png, 'png') std_out, std_err = p.communicate(input=temp_png.getvalue()) if len(std_out) == 0: return False if std_err: raise Exception("zbarimg", std_err.strip()) #p = subprocess.Popen(['iconv', '-f', 'UTF-8', '-t', 'ISO-8859-1'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) #std_out, std_err = p.communicate(input=std_out) #if std_err: # raise Exception("iconv", std_err.strip()) try: self.indata = {'body': b32decode(std_out.rstrip().replace('/', '='))} self.write_tun() except: pass def read_cam(self): rval, frame = self.vc.read() if not rval: return False self.inframe = scipy.misc.toimage(frame).convert('L') print "CAM" return True def run(self): self.running = True while self.running: if self.read_tun() or self.display_cam: self.write_qrcode() if not self.read_cam(): running = False break self.read_qrcode() event = pygame.event.poll() if event.type == pygame.QUIT: self.running = False pygame.quit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: self.running = False pygame.quit() elif event.key == pygame.K_UP: self.scale += 1 pygame.display.set_caption("qrtun - QR Code scale %d"%(self.scale)) self.write_qrcode() elif event.key == pygame.K_DOWN: self.scale -= 1 pygame.display.set_caption("qrtun - QR Code scale %d"%(self.scale)) self.write_qrcode() elif event.key == pygame.K_SPACE: self.display_cam = not self.display_cam self.write_qrcode() try: #os.unlink(self.outfile) os.unlink(self.infile) except: pass def main(): if len(sys.argv) < 2 or sys.argv[1] not in ['1', '2']: print("Must specify side 1 or 2 of tunnel") sys.exit(0) tun = QRTun(sys.argv[1]) def signal_handler(signal, frame): print('Shutting down') tun.running = False signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) try: os.mkdir("resources") except: pass tun.run() sys.exit(0) if __name__ == "__main__": main()