Changeset 186
- Timestamp:
- 07/30/12 01:06:26 (12 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
etherws/trunk/etherws.py
r185 r186 2 2 # -*- coding: utf-8 -*- 3 3 # 4 # Ethernet over WebSocket tunneling server/client4 # Ethernet over WebSocket 5 5 # 6 6 # depends on: … … 9 9 # - websocket-client-0.7.0 10 10 # - tornado-2.3 11 #12 # todo:13 # - servant mode support (like typical p2p software)14 11 # 15 12 # =========================================================================== … … 177 174 return sorted(self._table.itervalues(), cmp=SwitchPort.cmp_by_number) 178 175 179 def shut_port(self, portnum, flag=True):180 self._table[portnum].shut = flag181 182 176 def get_port(self, portnum): 183 177 return self._table[portnum] 184 178 185 179 def register_port(self, interface): 186 interface._switch_portnum = self._next # XXX 187 self._table[self._next] = SwitchPort(self._next, interface) 188 self._next += 1 180 try: 181 interface._switch_portnum = self._next # XXX 182 self._table[self._next] = SwitchPort(self._next, interface) 183 return self._next 184 finally: 185 self._next += 1 189 186 190 187 def unregister_port(self, interface): … … 307 304 308 305 306 class EtherWebSocketHandler(DebugMixIn, BasicAuthMixIn, WebSocketHandler): 307 def __init__(self, app, req, switch, htpasswd=None, debug=False): 308 super(EtherWebSocketHandler, self).__init__(app, req) 309 self._switch = switch 310 self._htpasswd = htpasswd 311 self._debug = debug 312 313 @classmethod 314 def get_type(cls): 315 return 'server' 316 317 def get_target(self): 318 return self.request.remote_ip 319 320 def open(self): 321 try: 322 return self._switch.register_port(self) 323 finally: 324 self.dprintf('connected: %s\n', lambda: self.request.remote_ip) 325 326 def on_message(self, message): 327 self._switch.receive(self, EthernetFrame(message)) 328 329 def on_close(self): 330 self._switch.unregister_port(self) 331 self.dprintf('disconnected: %s\n', lambda: self.request.remote_ip) 332 333 309 334 class TapHandler(DebugMixIn): 310 335 READ_SIZE = 65535 … … 317 342 self._tap = None 318 343 319 @property 320 def closed(self): 321 return not self._tap 322 323 def get_type(self): 344 @classmethod 345 def get_type(cls): 324 346 return 'tap' 325 347 326 def get_ name(self):348 def get_target(self): 327 349 if self.closed: 328 350 return self._dev 329 351 return self._tap.name 352 353 @property 354 def closed(self): 355 return not self._tap 330 356 331 357 def open(self): … … 334 360 self._tap = TunTapDevice(self._dev, IFF_TAP | IFF_NO_PI) 335 361 self._tap.up() 336 self._switch.register_port(self)337 362 self._ioloop.add_handler(self.fileno(), self, self._ioloop.READ) 363 return self._switch.register_port(self) 338 364 339 365 def close(self): 340 366 if self.closed: 341 367 raise ValueError('I/O operation on closed tap') 368 self._switch.unregister_port(self) 342 369 self._ioloop.remove_handler(self.fileno()) 343 self._switch.unregister_port(self)344 370 self._tap.close() 345 371 self._tap = None … … 374 400 375 401 376 class EtherWebSocketHandler(DebugMixIn, BasicAuthMixIn, WebSocketHandler):377 def __init__(self, app, req, switch, htpasswd=None, debug=False):378 super(EtherWebSocketHandler, self).__init__(app, req)379 self._switch = switch380 self._htpasswd = htpasswd381 self._debug = debug382 383 def get_type(self):384 return 'server'385 386 def get_name(self):387 return self.request.remote_ip388 389 def open(self):390 self._switch.register_port(self)391 self.dprintf('connected: %s\n', lambda: self.request.remote_ip)392 393 def on_message(self, message):394 self._switch.receive(self, EthernetFrame(message))395 396 def on_close(self):397 self._switch.unregister_port(self)398 self.dprintf('disconnected: %s\n', lambda: self.request.remote_ip)399 400 401 402 class EtherWebSocketClient(DebugMixIn): 402 403 def __init__(self, ioloop, switch, url, ssl_=None, cred=None, debug=False): … … 414 415 self._options['header'] = auth 415 416 417 @classmethod 418 def get_type(cls): 419 return 'client' 420 421 def get_target(self): 422 return self._url 423 416 424 @property 417 425 def closed(self): 418 426 return not self._sock 419 420 def get_type(self):421 return 'client'422 423 def get_name(self):424 return self._url425 427 426 428 def open(self): … … 436 438 self._sock = websocket.WebSocket() 437 439 self._sock.connect(self._url, **self._options) 438 self._switch.register_port(self)439 440 self._ioloop.add_handler(self.fileno(), self, self._ioloop.READ) 440 self.dprintf('connected: %s\n', lambda: self._url)441 return self._switch.register_port(self) 441 442 finally: 442 443 websocket._SSLSocketWrapper = sslwrap 444 self.dprintf('connected: %s\n', lambda: self._url) 443 445 444 446 def close(self): 445 447 if self.closed: 446 448 raise websocket.WebSocketException('already closed') 449 self._switch.unregister_port(self) 447 450 self._ioloop.remove_handler(self.fileno()) 448 self._switch.unregister_port(self)449 451 self._sock.close() 450 452 self._sock = None … … 478 480 class EtherWebSocketControlHandler(DebugMixIn, BasicAuthMixIn, RequestHandler): 479 481 NAMESPACE = 'etherws.control' 482 INTERFACES = { 483 TapHandler.get_type(): TapHandler, 484 EtherWebSocketClient.get_type(): EtherWebSocketClient, 485 } 480 486 481 487 def __init__(self, app, req, ioloop, switch, htpasswd=None, debug=False): … … 512 518 list_ = [] 513 519 for port in self._switch.portlist: 514 list_.append({ 515 'port': port.number, 516 'type': port.interface.get_type(), 517 'name': port.interface.get_name(), 518 'tx': port.tx, 519 'rx': port.rx, 520 'shut': port.shut, 521 }) 520 list_.append(self._portstat(port)) 522 521 return {'portlist': list_} 523 522 524 523 def handle_addPort(self, params): 524 list_ = [] 525 525 for p in params: 526 getattr(self, '_openport_' + p['type'])(p) 527 return self.handle_listPort(params) 526 type_ = p['type'] 527 target = p['target'] 528 options = getattr(self, '_optparse_' + type_)(p.get('options', {})) 529 klass = self.INTERFACES[type_] 530 interface = klass(self._ioloop, self._switch, target, **options) 531 portnum = interface.open() 532 list_.append(self._portstat(self._switch.get_port(portnum))) 533 return {'portlist': list_} 528 534 529 535 def handle_delPort(self, params): 536 list_ = [] 530 537 for p in params: 531 self._switch.get_port(int(p['port'])).interface.close() 532 return self.handle_listPort(params) 538 port = self._switch.get_port(int(p['port'])) 539 list_.append(self._portstat(port)) 540 port.interface.close() 541 return {'portlist': list_} 533 542 534 543 def handle_shutPort(self, params): 544 list_ = [] 535 545 for p in params: 536 self._switch.shut_port(int(p['port']), bool(p['flag']))537 return self.handle_listPort(params)538 539 def _openport_tap(self, p):540 dev = p['device'] 541 tap = TapHandler(self._ioloop, self._switch, dev, debug=self._debug)542 tap.open()543 544 def _op enport_client(self, p):545 ssl_ = self._ssl_wrapper(p.get('insecure'), p.get('cacerts'))546 cred = {'user': p.get('user'), 'passwd': p.get('passwd')}547 url = p['url']548 client = EtherWebSocketClient(self._ioloop, self._switch,549 url, ssl_, cred, self._debug)550 client.open()546 port = self._switch.get_port(int(p['port'])) 547 port.shut = bool(p['flag']) 548 list_.append(self._portstat(port)) 549 return {'portlist': list_} 550 551 def _optparse_tap(self, opt): 552 return {'debug': self._debug} 553 554 def _optparse_client(self, opt): 555 args = {'cert_reqs': ssl.CERT_REQUIRED, 'ca_certs': opt.get('cacerts')} 556 if opt.get('insecure'): 557 args = {} 558 ssl_ = lambda sock: ssl.wrap_socket(sock, **args) 559 cred = {'user': opt.get('user'), 'passwd': opt.get('passwd')} 560 return {'ssl_': ssl_, 'cred': cred, 'debug': self._debug} 551 561 552 562 @staticmethod 553 def _ssl_wrapper(insecure, ca_certs): 554 args = {'cert_reqs': ssl.CERT_REQUIRED, 'ca_certs': ca_certs} 555 if insecure: 556 args = {} 557 return lambda sock: ssl.wrap_socket(sock, **args) 558 559 560 def daemonize(nochdir=False, noclose=False): 561 if os.fork() > 0: 562 sys.exit(0) 563 564 os.setsid() 565 566 if os.fork() > 0: 567 sys.exit(0) 568 569 if not nochdir: 570 os.chdir('/') 571 572 if not noclose: 573 os.umask(0) 574 sys.stdin.close() 575 sys.stdout.close() 576 sys.stderr.close() 577 os.close(0) 578 os.close(1) 579 os.close(2) 580 sys.stdin = open(os.devnull) 581 sys.stdout = open(os.devnull, 'a') 582 sys.stderr = open(os.devnull, 'a') 583 584 585 def main(): 586 def realpath(ns, *keys): 587 for k in keys: 588 v = getattr(ns, k, None) 589 if v is not None: 590 v = os.path.realpath(v) 591 open(v).close() # check readable 592 setattr(ns, k, v) 593 594 def checkpath(ns, path): 563 def _portstat(port): 564 return { 565 'port': port.number, 566 'type': port.interface.get_type(), 567 'target': port.interface.get_target(), 568 'tx': port.tx, 569 'rx': port.rx, 570 'shut': port.shut, 571 } 572 573 574 def start_switch(args): 575 def daemonize(nochdir=False, noclose=False): 576 if os.fork() > 0: 577 sys.exit(0) 578 579 os.setsid() 580 581 if os.fork() > 0: 582 sys.exit(0) 583 584 if not nochdir: 585 os.chdir('/') 586 587 if not noclose: 588 os.umask(0) 589 sys.stdin.close() 590 sys.stdout.close() 591 sys.stderr.close() 592 os.close(0) 593 os.close(1) 594 os.close(2) 595 sys.stdin = open(os.devnull) 596 sys.stdout = open(os.devnull, 'a') 597 sys.stderr = open(os.devnull, 'a') 598 599 def checkabspath(ns, path): 595 600 val = getattr(ns, path, '') 596 601 if not val.startswith('/'): … … 606 611 return None 607 612 613 def setrealpath(ns, *keys): 614 for k in keys: 615 v = getattr(ns, k, None) 616 if v is not None: 617 v = os.path.realpath(v) 618 open(v).close() # check readable 619 setattr(ns, k, v) 620 608 621 def setport(ns, port, isssl): 609 622 val = getattr(ns, port, None) … … 620 633 return setattr(ns, htpasswd, Htpasswd(val)) 621 634 622 parser = argparse.ArgumentParser()623 624 parser.add_argument('--debug', action='store_true', default=False)625 parser.add_argument('--foreground', action='store_true', default=False)626 parser.add_argument('--ageout', action='store', type=int, default=300)627 628 parser.add_argument('--path', action='store', default='/')629 parser.add_argument('--host', action='store', default='')630 parser.add_argument('--port', action='store', type=int)631 parser.add_argument('--htpasswd', action='store')632 parser.add_argument('--sslkey', action='store')633 parser.add_argument('--sslcert', action='store')634 635 parser.add_argument('--ctlpath', action='store', default='/ctl')636 parser.add_argument('--ctlhost', action='store', default='')637 parser.add_argument('--ctlport', action='store', type=int)638 parser.add_argument('--ctlhtpasswd', action='store')639 parser.add_argument('--ctlsslkey', action='store')640 parser.add_argument('--ctlsslcert', action='store')641 642 args = parser.parse_args()643 644 635 #if args.debug: 645 636 # websocket.enableTrace(True) … … 648 639 raise ValueError('invalid ageout: %s' % args.ageout) 649 640 650 realpath(args, 'htpasswd', 'sslkey', 'sslcert')651 realpath(args, 'ctlhtpasswd', 'ctlsslkey', 'ctlsslcert')652 653 check path(args, 'path')654 check path(args, 'ctlpath')641 setrealpath(args, 'htpasswd', 'sslkey', 'sslcert') 642 setrealpath(args, 'ctlhtpasswd', 'ctlsslkey', 'ctlsslcert') 643 644 checkabspath(args, 'path') 645 checkabspath(args, 'ctlpath') 655 646 656 647 sslopt = getsslopt(args, 'sslkey', 'sslcert') … … 715 706 716 707 708 def main(): 709 parser = argparse.ArgumentParser() 710 subparsers = parser.add_subparsers(dest='subcommand') 711 parser_s = subparsers.add_parser('switch') 712 parser_c = subparsers.add_parser('control') 713 714 parser_s.add_argument('--debug', action='store_true', default=False) 715 parser_s.add_argument('--foreground', action='store_true', default=False) 716 parser_s.add_argument('--ageout', action='store', type=int, default=300) 717 718 parser_s.add_argument('--path', action='store', default='/') 719 parser_s.add_argument('--host', action='store', default='') 720 parser_s.add_argument('--port', action='store', type=int) 721 parser_s.add_argument('--htpasswd', action='store') 722 parser_s.add_argument('--sslkey', action='store') 723 parser_s.add_argument('--sslcert', action='store') 724 725 parser_s.add_argument('--ctlpath', action='store', default='/ctl') 726 parser_s.add_argument('--ctlhost', action='store', default='') 727 parser_s.add_argument('--ctlport', action='store', type=int) 728 parser_s.add_argument('--ctlhtpasswd', action='store') 729 parser_s.add_argument('--ctlsslkey', action='store') 730 parser_s.add_argument('--ctlsslcert', action='store') 731 732 args = parser.parse_args() 733 734 globals()['start_' + args.subcommand](args) 735 736 717 737 if __name__ == '__main__': 718 738 main()
Note: See TracChangeset
for help on using the changeset viewer.