/emulcomm.py
Python | 2239 lines | 1860 code | 120 blank | 259 comment | 89 complexity | 3e72f39803a24e384708e4ae476268fa MD5 | raw file
Large files files are truncated, but you can click here to view the full file
1""" 2 Author: Justin Cappos, Armon Dadgar 3 4 Start Date: 27 June 2008 5 6 Description: 7 8 This is a collection of communications routines that provide a programmer 9 with a reasonable environment. This is used by repy.py to provide a 10 highly restricted (but usable) environment. 11""" 12 13import socket 14 15# Armon: Used to check if a socket is ready 16import select 17 18# socket uses getattr and setattr. We need to make these available to it... 19socket.getattr = getattr 20socket.setattr = setattr 21 22 23# needed to set threads for recvmess and waitforconn 24import threading 25# threading in python2.7 uses hasattr. It needs to be made available. 26threading.hasattr = hasattr 27 28 29# So I can exit all threads when an error occurs or do select 30import harshexit 31 32# Needed for finding out info about sockets, available interfaces, etc 33import nonportable 34 35# So I can print a clean traceback when an error happens 36import tracebackrepy 37 38# accounting 39# id(sock) will be used to register and unregister sockets with nanny 40import nanny 41 42# give me uniqueIDs for the comminfo table 43import idhelper 44 45# for sleep 46import time 47 48# Armon: Used for decoding the error messages 49import errno 50 51# Armon: Used for getting the constant IP values for resolving our external IP 52import repy_constants 53 54# Get the exceptions 55from exception_hierarchy import * 56 57###### Module Data 58 59# This is a library of all currently bound sockets. Since multiple 60# UDP bindings on a single port is hairy, we store bound sockets 61# here, and use them for both sending and receiving if they are 62# available. This feels slightly byzantine, but it allows us to 63# avoid modifying the repy API. 64# 65# Format of entries is as follows: 66# Key - 3-tuple of ("UDP", IP, Port) 67# Val - Bound socket object 68_BOUND_SOCKETS = {} # Ticket = 1015 (Resolved) 69 70# If we have a preference for an IP/Interface this flag is set to True 71user_ip_interface_preferences = False 72 73# Do we allow non-specified IPs 74allow_nonspecified_ips = True 75 76# Armon: Specified the list of allowed IP and Interfaces in order of their preference 77# The basic structure is list of tuples (IP, Value), IP is True if its an IP, False if its an interface 78user_specified_ip_interface_list = [] 79 80# This list caches the allowed IP's 81# It is updated at the launch of repy, or by calls to getmyip and update_ip_cache 82# NOTE: The loopback address 127.0.0.1 is always permitted. update_ip_cache will always add this 83# if it is not specified explicitly by the user 84allowediplist = [] 85cachelock = threading.Lock() # This allows only a single simultaneous cache update 86 87 88##### Internal Functions 89 90# Determines if a specified IP address is allowed in the context of user settings 91def _ip_is_allowed(ip): 92 """ 93 <Purpose> 94 Determines if a given IP is allowed, by checking against the cached allowed IP's. 95 96 <Arguments> 97 ip: The IP address to search for. 98 99 <Returns> 100 True, if allowed. False, otherwise. 101 """ 102 global allowediplist 103 global user_ip_interface_preferences 104 global allow_nonspecified_ips 105 106 # If there is no preference, anything goes 107 # same with allow_nonspecified_ips 108 if not user_ip_interface_preferences or allow_nonspecified_ips: 109 return True 110 111 # Check the list of allowed IP's 112 return (ip in allowediplist) 113 114 115# Only appends the elem to lst if the elem is unique 116def _unique_append(lst, elem): 117 if elem not in lst: 118 lst.append(elem) 119 120# This function updates the allowed IP cache 121# It iterates through all possible IP's and stores ones which are bindable as part of the allowediplist 122def update_ip_cache(): 123 global allowediplist 124 global user_ip_interface_preferences 125 global user_specified_ip_interface_list 126 global allow_nonspecified_ips 127 128 # If there is no preference, this is a no-op 129 if not user_ip_interface_preferences: 130 return 131 132 # Acquire the lock to update the cache 133 cachelock.acquire() 134 135 # If there is any exception release the cachelock 136 try: 137 # Stores the IP's 138 allowed_list = [] 139 140 # Iterate through the allowed list, handle each element 141 for (is_ip_addr, value) in user_specified_ip_interface_list: 142 # Handle normal IP's 143 if is_ip_addr: 144 _unique_append(allowed_list, value) 145 146 # Handle interfaces 147 else: 148 try: 149 # Get the IP's associated with the NIC 150 interface_ips = nonportable.os_api.get_interface_ip_addresses(value) 151 for interface_ip in interface_ips: 152 _unique_append(allowed_list, interface_ip) 153 except: 154 # Catch exceptions if the NIC does not exist 155 pass 156 157 # This will store all the IP's that we are able to bind to 158 bindable_list = [] 159 160 # Try binding to every ip 161 for ip in allowed_list: 162 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 163 try: 164 sock.bind((ip,0)) 165 except: 166 pass # Not a good ip, skip it 167 else: 168 bindable_list.append(ip) # This is a good ip, store it 169 finally: 170 sock.close() 171 172 # Add loopback 173 _unique_append(bindable_list, "127.0.0.1") 174 175 # Update the global cache 176 allowediplist = bindable_list 177 178 finally: 179 # Release the lock 180 cachelock.release() 181 182 183############## General Purpose socket functions ############## 184 185def _is_already_connected_exception(exceptionobj): 186 """ 187 <Purpose> 188 Determines if a given error number indicates that the socket 189 is already connected. 190 191 <Arguments> 192 An exception object from a network call. 193 194 <Returns> 195 True if already connected, false otherwise 196 """ 197 # Get the type 198 exception_type = type(exceptionobj) 199 200 # Only continue if the type is socket.error 201 if exception_type is not socket.error: 202 return False 203 204 # Get the error number 205 errnum = exceptionobj[0] 206 207 # Store a list of error messages meaning we are connected 208 connected_errors = ["EISCONN", "WSAEISCONN"] 209 210 # Convert the errno to and error string name 211 try: 212 errname = errno.errorcode[errnum] 213 except Exception,e: 214 # The error is unknown for some reason... 215 errname = None 216 217 # Return if the error name is in our white list 218 return (errname in connected_errors) 219 220 221def _is_addr_in_use_exception(exceptionobj): 222 """ 223 <Purpose> 224 Determines if a given error number indicates that the provided 225 localip / localport are already bound and that the unique 226 tuple is already in use. 227 228 <Arguments> 229 An exception object from a network call. 230 231 <Returns> 232 True if already in use, false otherwise 233 """ 234 # Get the type 235 exception_type = type(exceptionobj) 236 237 # Only continue if the type is socket.error 238 if exception_type is not socket.error: 239 return False 240 241 # Get the error number 242 errnum = exceptionobj[0] 243 244 # Store a list of error messages meaning we are in use 245 in_use_errors = ["EADDRINUSE", "WSAEADDRINUSE"] 246 247 # Convert the errno to and error string name 248 try: 249 errname = errno.errorcode[errnum] 250 except Exception,e: 251 # The error is unknown for some reason... 252 errname = None 253 254 # Return if the error name is in our white list 255 return (errname in in_use_errors) 256 257 258def _is_addr_unavailable_exception(exceptionobj): 259 """ 260 <Purpose> 261 Determines if a given error number indicates that the provided 262 localip is not available during a bind() call. 263 This indicates an AddressBindingError should be raised. 264 265 <Arguments> 266 An exception object from a network call. 267 268 <Returns> 269 True if already in use, false otherwise 270 """ 271 # Get the type 272 exception_type = type(exceptionobj) 273 274 # Only continue if the type is socket.error 275 if exception_type is not socket.error: 276 return False 277 278 # Get the error number 279 errnum = exceptionobj[0] 280 281 # Store a list of error messages meaning the address is not available 282 not_avail_errors = ["EADDRNOTAVAIL", "WSAEADDRNOTAVAIL"] 283 284 # Convert the errno to and error string name 285 try: 286 errname = errno.errorcode[errnum] 287 except Exception,e: 288 # The error is unknown for some reason... 289 errname = None 290 291 # Return if the error name is in our white list 292 return (errname in not_avail_errors) 293 294 295def _is_conn_refused_exception(exceptionobj): 296 """ 297 <Purpose> 298 Determines if a given error number indicates that the remote 299 host has actively refused the connection. E.g. 300 ECONNREFUSED 301 302 <Arguments> 303 An exception object from a network call. 304 305 <Returns> 306 True if the error indicates the connection was refused, false otherwise 307 """ 308 # Get the type 309 exception_type = type(exceptionobj) 310 311 # Only continue if the type is socket.error 312 if exception_type is not socket.error: 313 return False 314 315 # Get the error number 316 errnum = exceptionobj[0] 317 318 # Store a list of error messages meaning the host refused 319 refused_errors = ["ECONNREFUSED", "WSAECONNREFUSED"] 320 321 # Convert the errno to and error string name 322 try: 323 errname = errno.errorcode[errnum] 324 except Exception,e: 325 # The error is unknown for some reason... 326 errname = None 327 328 # Return if the error name is in our white list 329 return (errname in refused_errors) 330 331 332def _is_network_down_exception(exceptionobj): 333 """ 334 <Purpose> 335 Determines if a given error number indicates that the 336 network is down. 337 338 <Arguments> 339 An exception object from a network call. 340 341 <Returns> 342 True if the network is down, false otherwise 343 """ 344 # Get the type 345 exception_type = type(exceptionobj) 346 347 # Only continue if the type is socket.error 348 if exception_type is not socket.error: 349 return False 350 351 # Get the error number 352 errnum = exceptionobj[0] 353 354 # Store a list of error messages meaning we are disconnected 355 net_down_errors = ["ENETDOWN","ENETUNREACH","WSAENETDOWN", "WSAENETUNREACH"] 356 357 # Convert the errno to and error string name 358 try: 359 errname = errno.errorcode[errnum] 360 except Exception,e: 361 # The error is unknown for some reason... 362 errname = None 363 364 # Return if the error name is in our white list 365 return (errname in net_down_errors) 366 367 368def _is_recoverable_network_exception(exceptionobj): 369 """ 370 <Purpose> 371 Determines if a given error number is recoverable or fatal. 372 373 <Arguments> 374 An exception object from a network call. 375 376 <Returns> 377 True if potentially recoverable, False if fatal. 378 """ 379 # Get the type 380 exception_type = type(exceptionobj) 381 382 # socket.timeout is recoverable always 383 if exception_type == socket.timeout: 384 return True 385 386 # Only continue if the type is socket.error or select.error 387 elif exception_type != socket.error and exception_type != select.error: 388 return False 389 390 # Get the error number 391 errnum = exceptionobj[0] 392 393 # Store a list of recoverable error numbers 394 recoverable_errors = ["EINTR","EAGAIN","EBUSY","EWOULDBLOCK","ETIMEDOUT","ERESTART", 395 "WSAEINTR","WSAEWOULDBLOCK","WSAETIMEDOUT","EALREADY","WSAEALREADY", 396 "EINPROGRESS","WSAEINPROGRESS"] 397 398 # Convert the errno to and error string name 399 try: 400 errname = errno.errorcode[errnum] 401 except Exception,e: 402 # The error is unknown for some reason... 403 errname = None 404 405 # Return if the error name is in our white list 406 return (errname in recoverable_errors) 407 408 409# Determines based on exception if the connection has been terminated 410def _is_terminated_connection_exception(exceptionobj): 411 """ 412 <Purpose> 413 Determines if the exception is indicated the connection is terminated. 414 415 <Arguments> 416 An exception object from a network call. 417 418 <Returns> 419 True if the connection is terminated, False otherwise. 420 False means we could not determine with certainty if the socket is closed. 421 """ 422 # Get the type 423 exception_type = type(exceptionobj) 424 425 # We only want to continue if it is socket.error or select.error 426 if exception_type != socket.error and exception_type != select.error: 427 return False 428 429 # Get the error number 430 errnum = exceptionobj[0] 431 432 # Store a list of errors which indicate connection closed 433 connection_closed_errors = ["EPIPE","EBADF","EBADR","ENOLINK","EBADFD","ENETRESET", 434 "ECONNRESET","WSAEBADF","WSAENOTSOCK","WSAECONNRESET",] 435 436 # Convert the errnum to an error string 437 try: 438 errname = errno.errorcode[errnum] 439 except: 440 # The error number is not defined... 441 errname = None 442 443 # Return whether the errname is in our pre-defined list 444 return (errname in connection_closed_errors) 445 446 447 448# Armon: This is used for semantics, to determine if we have a valid IP. 449def _is_valid_ip_address(ipaddr): 450 """ 451 <Purpose> 452 Determines if ipaddr is a valid IP address. 453 0.X and 224-255.X addresses are not allowed. 454 Additionally, 192.168.0.0 is not allowed. 455 456 <Arguments> 457 ipaddr: String to check for validity. (It will check that this is a string). 458 459 <Returns> 460 True if a valid IP, False otherwise. 461 """ 462 # Argument must be of the string type 463 if not type(ipaddr) == str: 464 return False 465 466 if ipaddr == '192.168.0.0': 467 return False 468 469 # A valid IP should have 4 segments, explode on the period 470 octets = ipaddr.split(".") 471 472 # Check that we have 4 parts 473 if len(octets) != 4: 474 return False 475 476 # Check that each segment is a number between 0 and 255 inclusively. 477 for octet in octets: 478 # Attempt to convert to an integer 479 try: 480 ipnumber = int(octet) 481 except ValueError: 482 # There was an error converting to an integer, not an IP 483 return False 484 485 # IP addresses octets must be between 0 and 255 486 if not (ipnumber >= 0 and ipnumber <= 255): 487 return False 488 489 # should not have a ValueError (I already checked) 490 firstipnumber = int(octets[0]) 491 492 # IP addresses with the first octet 0 refer to all local IPs. These are 493 # not allowed 494 if firstipnumber == 0: 495 return False 496 497 # IP addresses with the first octet >=224 are either Multicast or reserved. 498 # These are not allowed 499 if firstipnumber >= 224: 500 return False 501 502 # At this point, assume the IP is valid 503 return True 504 505 506# Armon: This is used for semantics, to determine if the given port is valid 507def _is_valid_network_port(port): 508 """ 509 <Purpose> 510 Determines if a given network port is valid. 511 512 <Arguments> 513 port: A numeric type (this will be checked) port number. 514 515 <Returns> 516 True if valid, False otherwise. 517 """ 518 # Check the type is int or long 519 if not (type(port) == long or type(port) == int): 520 return False 521 522 if port >= 1 and port <= 65535: 523 return True 524 else: 525 return False 526 527 528# Used to decide if an IP is the loopback IP or not. This is needed for 529# accounting 530def _is_loopback_ipaddr(host): 531 if not host.startswith('127.'): 532 return False 533 if len(host.split('.')) != 4: 534 return False 535 536 octets = host.split('.') 537 if len(octets) != 4: 538 return False 539 for octet in octets: 540 try: 541 if int(octet) > 255 or int(octet) < 0: 542 return False 543 except ValueError: 544 return False 545 546 return True 547 548 549# Checks if binding to the local port is allowed 550# type should be "TCP" or "UDP". 551def _is_allowed_localport(type, localport): 552 # Switch to the proper resource 553 if type == "TCP": 554 resource = "connport" 555 elif type == "UDP": 556 resource = "messport" 557 else: 558 raise InternalRepyError("Bad type specified for _is_allowed_localport()") 559 560 # Check what is allowed by nanny 561 return nanny.is_item_allowed(resource, float(localport)) 562 563 564 565 566######################### Simple Public Functions ########################## 567 568 569 570# Public interface 571def gethostbyname(name): 572 """ 573 <Purpose> 574 Provides information about a hostname. Calls socket.gethostbyname(). 575 Translate a host name to IPv4 address format. The IPv4 address is 576 returned as a string, such as '100.50.200.5'. If the host name is an 577 IPv4 address itself it is returned unchanged. 578 579 <Arguments> 580 name: 581 The host name to translate. 582 583 <Exceptions> 584 RepyArgumentError (descends from NetworkError) if the name is not a string 585 NetworkAddressError (descends from NetworkError) if the address cannot 586 be resolved. 587 588 <Side Effects> 589 None. 590 591 <Resource Consumption> 592 This operation consumes network bandwidth of 4K netrecv, 1K netsend. 593 (It's hard to tell how much was actually sent / received at this level.) 594 595 <Returns> 596 The IPv4 address as a string. 597 """ 598 599 if type(name) is not str: 600 raise RepyArgumentError("gethostbyname() takes a string as argument.") 601 602 # charge 4K for a look up... I don't know the right number, but we should 603 # charge something. We'll always charge to the netsend interface... 604 nanny.tattle_quantity('netsend', 1024) 605 nanny.tattle_quantity('netrecv', 4096) 606 607 try: 608 return socket.gethostbyname(name) 609 except socket.gaierror: 610 raise NetworkAddressError("The hostname '"+name+"' could not be resolved.") 611 612 613 614# Public interface 615def getmyip(): 616 """ 617 <Purpose> 618 Provides the IP of this computer on its public facing interface. 619 Does some clever trickery. 620 621 <Arguments> 622 None 623 624 <Exceptions> 625 InternetConnectivityError is the host is not connected to the internet. 626 627 <Side Effects> 628 None. 629 630 <Resource Consumption> 631 This operations consumes 256 netsend and 128 netrecv. 632 633 <Returns> 634 The localhost's IP address 635 """ 636 # Charge for the resources 637 nanny.tattle_quantity("netsend", 256) 638 nanny.tattle_quantity("netrecv", 128) 639 640 # I got some of this from: http://groups.google.com/group/comp.lang.python/browse_thread/thread/d931cdc326d7032b?hl=en 641 642 # Update the cache and return the first allowed IP 643 # Only if a preference is set 644 if user_ip_interface_preferences: 645 update_ip_cache() 646 # Return the first allowed ip, there is always at least 1 element (loopback) 647 return allowediplist[0] 648 649 # Initialize these to None, so we can detect a failure 650 myip = None 651 652 # It's possible on some platforms (Windows Mobile) that the IP will be 653 # 0.0.0.0 even when I have a public IP and the external IP is up. However, if 654 # I get a real connection with SOCK_STREAM, then I should get the real 655 # answer. 656 657 # Try each stable IP 658 for ip_addr in repy_constants.STABLE_PUBLIC_IPS: 659 try: 660 # Try to resolve using the current connection type and 661 # stable IP, using port 80 since some platforms panic 662 # when given 0 (FreeBSD) 663 myip = _get_localIP_to_remoteIP(socket.SOCK_DGRAM, ip_addr, 80) 664 except (socket.error, socket.timeout): 665 # We can ignore any networking related errors, since we want to try 666 # the other connection types and IP addresses. If we fail, 667 # we will eventually raise an exception anyways. 668 pass 669 else: 670 # Return immediately if the IP address is good 671 if _is_valid_ip_address(myip): 672 return myip 673 674 675 # Since we haven't returned yet, we must have failed. 676 # Raise an exception, we must not be connected to the internet 677 raise InternetConnectivityError("Cannot detect a connection to the Internet.") 678 679 680 681def _get_localIP_to_remoteIP(connection_type, external_ip, external_port=80): 682 """ 683 <Purpose> 684 Resolve the local ip used when connecting outbound to an external ip. 685 686 <Arguments> 687 connection_type: 688 The type of connection to attempt. See socket.socket(). 689 690 external_ip: 691 The external IP to attempt to connect to. 692 693 external_port: 694 The port on the remote host to attempt to connect to. 695 696 <Exceptions> 697 As with socket.socket(), socketobj.connect(), etc. 698 699 <Returns> 700 The locally assigned IP for the connection. 701 """ 702 # Open a socket 703 sockobj = socket.socket(socket.AF_INET, connection_type) 704 705 # Make sure that the socket obj doesn't hang forever in 706 # case connect() is blocking. Fix to #1003 707 sockobj.settimeout(1.0) 708 709 try: 710 sockobj.connect((external_ip, external_port)) 711 712 # Get the local connection information for this socket 713 (myip, localport) = sockobj.getsockname() 714 715 # Always close the socket 716 finally: 717 sockobj.close() 718 719 return myip 720 721 722 723 724###################### Shared message / connection items ################### 725 726 727 728 729# Armon: How frequently should we check for the availability of the socket? 730RETRY_INTERVAL = 0.2 # In seconds 731 732 733def _cleanup_socket(self): 734 """ 735 <Purpose> 736 Internal cleanup method for open sockets. The socket 737 lock for the socket should be acquired prior to 738 calling. 739 740 <Arguments> 741 None 742 <Side Effects> 743 The insocket/outsocket handle will be released. 744 745 <Exceptions> 746 InternalRepyError is raised if the socket lock is not held 747 prior to calling the function. 748 749 <Returns> 750 None 751 """ 752 sock = self.socketobj 753 socket_lock = self.sock_lock 754 # Make sure the lock is already acquired 755 # BUG: We don't know which thread exactly acquired the lock. 756 if socket_lock.acquire(False): 757 socket_lock.release() 758 raise InternalRepyError("Socket lock should be acquired before calling _cleanup_socket!") 759 760 if (sock == None): 761 # Already cleaned up 762 return 763 # Shutdown the socket for writing prior to close 764 # to unblock any threads that are writing 765 try: 766 sock.shutdown(socket.SHUT_WR) 767 except: 768 pass 769 770 # Close the socket 771 try: 772 sock.close() 773 except: 774 pass 775 # socket id is used to unregister socket with nanny 776 sockid = id(sock) 777 # Re-store resources 778 nanny.tattle_remove_item('insockets', sockid) 779 nanny.tattle_remove_item('outsockets', sockid) 780 781 782####################### Message sending ############################# 783 784 785 786# Public interface!!! 787def sendmessage(destip, destport, message, localip, localport): 788 """ 789 <Purpose> 790 Send a message to a host / port 791 792 <Arguments> 793 destip: 794 The host to send a message to 795 destport: 796 The port to send the message to 797 message: 798 The message to send 799 localhost: 800 The local IP to send the message from 801 localport: 802 The local port to send the message from 803 804 <Exceptions> 805 AddressBindingError (descends NetworkError) when the local IP isn't 806 a local IP. 807 808 ResourceForbiddenError (descends ResourceException?) when the local 809 port isn't allowed 810 811 RepyArgumentError when the local IP and port aren't valid types 812 or values 813 814 AlreadyListeningError if there is an existing listening UDP socket 815 on the same local IP and port. 816 817 DuplicateTupleError if there is another sendmessage on the same 818 local IP and port to the same remote host. 819 820 <Side Effects> 821 None. 822 823 <Resource Consumption> 824 This operation consumes 64 bytes + number of bytes of the message that 825 were transmitted. This requires that the localport is allowed. 826 827 <Returns> 828 The number of bytes sent on success 829 """ 830 # Check the input arguments (type) 831 if type(destip) is not str: 832 raise RepyArgumentError("Provided destip must be a string!") 833 if type(localip) is not str: 834 raise RepyArgumentError("Provided localip must be a string!") 835 836 if type(destport) is not int: 837 raise RepyArgumentError("Provided destport must be an int!") 838 if type(localport) is not int: 839 raise RepyArgumentError("Provided localport must be an int!") 840 841 if type(message) is not str: 842 raise RepyArgumentError("Provided message must be a string!") 843 844 845 # Check the input arguments (sanity) 846 if not _is_valid_ip_address(destip): 847 raise RepyArgumentError("Provided destip is not valid! IP: '"+destip+"'") 848 if not _is_valid_ip_address(localip): 849 raise RepyArgumentError("Provided localip is not valid! IP: '"+localip+"'") 850 851 if not _is_valid_network_port(destport): 852 raise RepyArgumentError("Provided destport is not valid! Port: "+str(destport)) 853 if not _is_valid_network_port(localport): 854 raise RepyArgumentError("Provided localport is not valid! Port: "+str(localport)) 855 856 857 # Check that if localip == destip, then localport != destport 858 if localip == destip and localport == destport: 859 raise RepyArgumentError("Local socket name cannot match destination socket name! Local/Dest IP and Port match.") 860 861 # Check the input arguments (permission) 862 update_ip_cache() 863 if not _ip_is_allowed(localip): 864 raise ResourceForbiddenError("Provided localip is not allowed! IP: "+localip) 865 866 if not _is_allowed_localport("UDP", localport): 867 raise ResourceForbiddenError("Provided localport is not allowed! Port: "+str(localport)) 868 869 # Wait for netsend 870 if _is_loopback_ipaddr(destip): 871 nanny.tattle_quantity('loopsend', 0) 872 else: 873 nanny.tattle_quantity('netsend', 0) 874 875 try: 876 sock = None 877 878 if ("UDP", localip, localport) in _BOUND_SOCKETS: 879 sock = _BOUND_SOCKETS[("UDP", localip, localport)] 880 else: 881 # Get the socket 882 sock = _get_udp_socket(localip, localport) 883 # Register this socket with nanny 884 nanny.tattle_add_item("outsockets", id(sock)) 885 # Send the message 886 bytessent = sock.sendto(message, (destip, destport)) 887 888 # Account for the resources 889 if _is_loopback_ipaddr(destip): 890 nanny.tattle_quantity('loopsend', bytessent + 64) 891 else: 892 nanny.tattle_quantity('netsend', bytessent + 64) 893 894 return bytessent 895 896 except Exception, e: 897 898 try: 899 # If we're borrowing the socket, closing is not appropriate. 900 if not ("UDP", localip, localport) in _BOUND_SOCKETS: 901 sock.close() 902 except: 903 pass 904 905 # Check if address is already in use 906 if _is_addr_in_use_exception(e): 907 raise DuplicateTupleError("Provided Local IP and Local Port is already in use!") 908 909 if _is_addr_unavailable_exception(e): 910 raise AddressBindingError("Cannot bind to the specified local ip, invalid!") 911 912 # Unknown error... 913 else: 914 raise 915 916 917 918 919# Public interface!!! 920def listenformessage(localip, localport): 921 """ 922 <Purpose> 923 Sets up a UDPServerSocket to receive incoming UDP messages. 924 925 <Arguments> 926 localip: 927 The local IP to register the handler on. 928 localport: 929 The port to listen on. 930 931 <Exceptions> 932 DuplicateTupleError (descends NetworkError) if the port cannot be 933 listened on because some other process on the system is listening on 934 it. 935 936 AlreadyListeningError if there is already a UDPServerSocket with the same 937 IP and port. 938 939 RepyArgumentError if the port number or ip is wrong type or obviously 940 invalid. 941 942 AddressBindingError (descends NetworkError) if the IP address isn't a 943 local IP. 944 945 ResourceForbiddenError if the port is not allowed. 946 947 <Side Effects> 948 Prevents other UDPServerSockets from using this port / IP 949 950 <Resource Consumption> 951 This operation consumes an insocket and requires that the provided messport is allowed. 952 953 <Returns> 954 The UDPServerSocket. 955 """ 956 # Check the input arguments (type) 957 if type(localip) is not str: 958 raise RepyArgumentError("Provided localip must be a string!") 959 960 if type(localport) is not int: 961 raise RepyArgumentError("Provided localport must be a int!") 962 963 964 # Check the input arguments (sanity) 965 if not _is_valid_ip_address(localip): 966 raise RepyArgumentError("Provided localip is not valid! IP: '"+localip+"'") 967 968 if not _is_valid_network_port(localport): 969 raise RepyArgumentError("Provided localport is not valid! Port: "+str(localport)) 970 971 972 # Check the input arguments (permission) 973 update_ip_cache() 974 if not _ip_is_allowed(localip): 975 raise ResourceForbiddenError("Provided localip is not allowed! IP: '"+localip+"'") 976 977 if not _is_allowed_localport("UDP", localport): 978 raise ResourceForbiddenError("Provided localport is not allowed! Port: "+str(localport)) 979 # This identity tuple will be used to check for an existing connection with same identity 980 identity = ("UDP", localip, localport, None, None) 981 982 try: 983 # Check if localip is on loopback 984 on_loopback = _is_loopback_ipaddr(localip) 985 986 # Get the socket 987 sock = _get_udp_socket(localip,localport) 988 989 # Register this socket as an insocket 990 nanny.tattle_add_item('insockets',id(sock)) 991 992 # Add the socket to _BOUND_SOCKETS so that we can 993 # preserve send functionality on this port. 994 _BOUND_SOCKETS[("UDP", localip, localport)] = sock 995 996 except Exception, e: 997 998 # Check if this an already in use error 999 if _is_addr_in_use_exception(e): 1000 # Call _conn_cleanup_check to determine if this is because 1001 # the socket is being cleaned up or if it is actively being used or 1002 # if there is an existing listening socket 1003 # This will always raise DuplicateTupleError or 1004 # CleanupInProgressError or AlreadyListeningError 1005 _conn_cleanup_check(identity) 1006 1007 # Check if this is a binding error 1008 if _is_addr_unavailable_exception(e): 1009 raise AddressBindingError("Cannot bind to the specified local ip, invalid!") 1010 1011 # Unknown error... 1012 else: 1013 raise 1014 1015 # Create a UDPServerSocket 1016 server_sock = UDPServerSocket(sock, on_loopback) 1017 1018 # Return the UDPServerSocket 1019 return server_sock 1020 1021 1022 1023####################### Connection oriented ############################# 1024def _conn_alreadyexists_check(identity): 1025 """ 1026 <Purpose> 1027 This private function checks if a socket that 1028 got EADDRINUSE is because the socket is active, 1029 or not 1030 1031 <Arguments> 1032 identity: A tuple to check for cleanup 1033 1034 <Exceptions> 1035 Raises DuplicateTupleError if the socket is actively being used. 1036 1037 Raises AddressBindingError if the binding is not allowed 1038 1039 <Returns> 1040 None 1041 """ 1042 # Decompose the tuple 1043 family, localip, localport, desthost, destport = identity 1044 1045 # Check the sockets status 1046 (exists, status) = nonportable.os_api.exists_outgoing_network_socket(localip,localport,desthost,destport) 1047 1048 # Check if the socket is actively being used 1049 # If the socket is these states: 1050 # ESTABLISHED : Connection is active 1051 # CLOSE_WAIT : Connection is closed, but waiting on local program to close 1052 # SYN_SENT (SENT) : Connection is just being established 1053 if exists and ("ESTABLISH" in status or "CLOSE_WAIT" in status or "SENT" in status): 1054 raise DuplicateTupleError("There is a duplicate connection which conflicts with the request!") 1055 1056 # Otherwise, the socket is being cleaned up 1057 raise AddressBindingError("Cannot bind to the specified local ip, invalid!") 1058 1059 1060def _conn_cleanup_check(identity): 1061 """ 1062 <Purpose> 1063 This private function checks if a socket that 1064 got EADDRINUSE is because the socket is active, 1065 or because the socket is listening or 1066 because the socket is being cleaned up. 1067 1068 <Arguments> 1069 identity: A tuple to check for cleanup 1070 1071 <Exceptions> 1072 Raises DuplicateTupleError if the socket is actively being used. 1073 1074 Raises AlreadyListeningError if the socket is listening. 1075 1076 Raises CleanupInProgressError if the socket is being cleaned up 1077 or if the socket does not appear to exist. This is because there 1078 may be a race between getting EADDRINUSE and the call to this 1079 function. 1080 1081 <Returns> 1082 None 1083 """ 1084 # Decompose the tuple 1085 family, localip, localport, desthost, destport = identity 1086 1087 # Check the sockets status 1088 (exists, status) = nonportable.os_api.exists_outgoing_network_socket(localip,localport,desthost,destport) 1089 1090 # Check if the socket is actively being used 1091 # If the socket is these states: 1092 # ESTABLISHED : Connection is active 1093 # CLOSE_WAIT : Connection is closed, but waiting on local program to close 1094 # SYN_SENT (SENT) : Connection is just being established 1095 if exists and ("ESTABLISH" in status or "CLOSE_WAIT" in status or "SENT" in status): 1096 raise DuplicateTupleError("There is a duplicate connection which conflicts with the request!") 1097 else: 1098 # Checking if a listening TCP or UDP socket exists with given local address 1099 # The third argument is True if socket type is TCP,False if socket type is UDP 1100 if (nonportable.os_api.exists_listening_network_socket(localip, localport, (family == "TCP"))): 1101 raise AlreadyListeningError("There is a listening socket on the provided localip and localport!") 1102 # Otherwise, the socket is being cleaned up 1103 else: 1104 raise CleanupInProgressError("The socket is being cleaned up by the operating system!") 1105 1106 1107def _timed_conn_initialize(localip,localport,destip,destport, timeout): 1108 """ 1109 <Purpose> 1110 Tries to initialize an outgoing socket to match 1111 the given address parameters. 1112 1113 <Arguments> 1114 localip,localport: The local address of the socket 1115 destip,destport: The destination address to which socket has to be connected 1116 timeout: Maximum time to try 1117 1118 <Exceptions> 1119 Raises TimeoutError if we timed out trying to connect. 1120 Raises ConnectionRefusedError if the connection was refused. 1121 Raises InternetConnectivityError if the network is down. 1122 1123 Raises any errors encountered calling _get_tcp_socket, 1124 or any non-recoverable network exception. 1125 1126 <Returns> 1127 A Python socket object connected to the dest, 1128 from the specified local tuple. 1129 """ 1130 1131 # Store our start time 1132 starttime = nonportable.getruntime() 1133 1134 # Get a TCP socket bound to the local ip / port 1135 sock = _get_tcp_socket(localip, localport) 1136 sock.settimeout(timeout) 1137 1138 try: 1139 # Try to connect until we timeout 1140 connected = False 1141 while nonportable.getruntime() - starttime < timeout: 1142 try: 1143 sock.connect((destip, destport)) 1144 connected = True 1145 break 1146 except Exception, e: 1147 # Check if we are already connected 1148 if _is_already_connected_exception(e): 1149 connected = True 1150 raise DuplicateTupleError("There is a duplicate connection which conflicts with the request!") 1151 break 1152 1153 # Check if the network is down 1154 if _is_network_down_exception(e): 1155 raise InternetConnectivityError("The network is down or cannot be reached from the local IP!") 1156 1157 # Check if the connection was refused 1158 if _is_conn_refused_exception(e): 1159 raise ConnectionRefusedError("The connection was refused!") 1160 1161 # Check if this is recoverable (try again, timeout, etc) 1162 elif not _is_recoverable_network_exception(e): 1163 raise 1164 1165 # Sleep and retry, avoid busy waiting 1166 time.sleep(RETRY_INTERVAL) 1167 1168 # Check if we timed out 1169 if not connected: 1170 raise TimeoutError("Timed-out connecting to the remote host!") 1171 1172 # Return the socket 1173 return sock 1174 1175 except: 1176 # Close the socket, and raise 1177 sock.close() 1178 raise 1179 1180 1181# Public interface!!! 1182def openconnection(destip, destport,localip, localport, timeout): 1183 """ 1184 <Purpose> 1185 Opens a connection, returning a socket-like object 1186 1187 1188 <Arguments> 1189 destip: The destination ip to open communications with 1190 1191 destport: The destination port to use for communication 1192 1193 localip: The local ip to use for the communication 1194 1195 localport: The local port to use for communication 1196 1197 timeout: The maximum amount of time to wait to connect. This may 1198 be a floating point number or an integer 1199 1200 1201 <Exceptions> 1202 1203 RepyArgumentError if the arguments are invalid. This includes both 1204 the types and values of arguments. If the localip matches the destip, 1205 and the localport matches the destport this will also be raised. 1206 1207 AddressBindingError (descends NetworkError) if the localip isn't 1208 associated with the local system or is not allowed. 1209 1210 ResourceForbiddenError (descends ResourceError) if the localport isn't 1211 allowed. 1212 1213 DuplicateTupleError (descends NetworkError) if the (localip, localport, 1214 destip, destport) tuple is already used. This will also occur if the 1215 operating system prevents the local IP / port from being used. 1216 1217 AlreadyListeningError if the (localip, localport) tuple is already used 1218 for a listening TCP socket. 1219 1220 CleanupInProgress if the (localip, localport, destip, destport) tuple is 1221 still being cleaned up by the OS. 1222 1223 ConnectionRefusedError (descends NetworkError) if the connection cannot 1224 be established because the destination port isn't being listened on. 1225 1226 TimeoutError (common to all API functions that timeout) if the 1227 connection times out 1228 1229 InternetConnectivityError if the network is down, or if the host 1230 cannot be reached from the local IP that has been bound to. 1231 1232 1233 <Side Effects> 1234 TODO 1235 1236 <Resource Consumption> 1237 This operation consumes 64*2 bytes of netsend (SYN, ACK) and 64 bytes 1238 of netrecv (SYN/ACK). This requires that the localport is allowed. Upon 1239 success, this call consumes an outsocket. 1240 1241 <Returns> 1242 A socket-like object that can be used for communication. Use send, 1243 recv, and close just like you would an actual socket object in python. 1244 """ 1245 # Check the input arguments (type) 1246 if type(destip) is not str: 1247 raise RepyArgumentError("Provided destip must be a string!") 1248 if type(localip) is not str: 1249 raise RepyArgumentError("Provided localip must be a string!") 1250 1251 if type(destport) is not int: 1252 raise RepyArgumentError("Provided destport must be an int!") 1253 if type(localport) is not int: 1254 raise RepyArgumentError("Provided localport must be an int!") 1255 1256 if type(timeout) not in [float, int]: 1257 raise RepyArgumentError("Provided timeout must be an int or float!") 1258 1259 1260 # Check the input arguments (sanity) 1261 if not _is_valid_ip_address(destip): 1262 raise RepyArgumentError("Provided destip is not valid! IP: '"+destip+"'") 1263 if not _is_valid_ip_address(localip): 1264 raise RepyArgumentError("Provided localip is not valid! IP: '"+localip+"'") 1265 1266 if not _is_valid_network_port(destport): 1267 raise RepyArgumentError("Provided destport is not valid! Port: "+str(destport)) 1268 if not _is_valid_network_port(localport): 1269 raise RepyArgumentError("Provided localport is not valid! Port: "+str(localport)) 1270 1271 if timeout <= 0: 1272 raise RepyArgumentError("Provided timeout is not valid, must be positive! Timeout: "+str(timeout)) 1273 1274 # Check that if localip == destip, then localport != destport 1275 if localip == destip and localport == destport: 1276 raise RepyArgumentError("Local socket name cannot match destination socket name! Local/Dest IP and Port match.") 1277 1278 # Check the input arguments (permission) 1279 update_ip_cache() 1280 if not _ip_is_allowed(localip): 1281 raise ResourceForbiddenError("Provided localip is not allowed! IP: "+localip) 1282 1283 if not _is_allowed_localport("TCP", localport): 1284 raise ResourceForbiddenError("Provided localport is not allowed! Port: "+str(localport)) 1285 1286 1287 1288 # use this tuple during connection clean up check 1289 identity = ("TCP", localip, localport, destip, destport) 1290 1291 # Wait for netsend / netrecv 1292 if _is_loopback_ipaddr(destip): 1293 nanny.tattle_quantity('loopsend', 0) 1294 nanny.tattle_quantity('looprecv', 0) 1295 else: 1296 nanny.tattle_quantity('netsend', 0) 1297 nanny.tattle_quantity('netrecv', 0) 1298 1299 try: 1300 # To Know if remote IP is on loopback or not 1301 on_loopback = _is_loopback_ipaddr(destip) 1302 1303 # Get the socket 1304 sock = _timed_conn_initialize(localip,localport,destip,destport, timeout) 1305 1306 # Register this socket as an outsocket 1307 nanny.tattle_add_item('outsockets',id(sock)) 1308 except Exception, e: 1309 1310 # Check if this an already in use error 1311 if _is_addr_in_use_exception(e): 1312 # Call _conn_cleanup_check to determine if this is because 1313 # the socket is being cleaned up or if it is actively being used 1314 # This will always raise DuplicateTupleError or 1315 # CleanupInProgressError or AlreadyListeningError 1316 _conn_cleanup_check(identity) 1317 1318 # Check if this is a binding error 1319 if _is_addr_unavailable_exception(e): 1320 # Call _conn_alreadyexists_check to determine if this is because 1321 # the connection is active or not 1322 _conn_alreadyexists_check(identity) 1323 1324 1325 # Unknown error... 1326 else: 1327 raise 1328 1329 emul_sock = EmulatedSocket(sock, on_loopback) 1330 1331 # Tattle the resources used 1332 if _is_loopback_ipaddr(destip): 1333 nanny.tattle_quantity('loopsend', 128) 1334 nanny.tattle_quantity('looprecv', 64) 1335 else: 1336 nanny.tattle_quantity('netsend', 128) 1337 nanny.tattle_quantity('netrecv', 64) 1338 1339 # Return the EmulatedSocket 1340 return emul_sock 1341 1342 1343def listenforconnection(localip, localport): 1344 """ 1345 <Purpose> 1346 Sets up a TCPServerSocket to recieve incoming TCP connections. 1347 1348 <Arguments> 1349 localip: 1350 The local IP to listen on 1351 localport: 1352 The local port to listen on 1353 1354 <Exceptions> 1355 Raises AlreadyListeningError if another TCPServerSocket or process has bound 1356 to the provided localip and localport. 1357 1358 Raises DuplicateTupleError if another process has bound to the 1359 provided localip and localport. 1360 1361 Raises RepyArgumentError if the localip or localport are invalid 1362 Raises ResourceForbiddenError if the ip or port is not allowed. 1363 Raises AddressBindingError if the IP address isn't a local ip. 1364 1365 <Side Effects> 1366 The IP / Port combination cannot be used until the TCPServerSocket 1367 is closed. 1368 1369 <Resource Consumption> 1370 Uses an insocket for the TCPServerSocket. 1371 1372 <Returns> 1373 A TCPServerSocket object. 1374 """ 1375 # Check the input arguments (type) 1376 if type(localip) is not str: 1377 raise RepyArgumentError("Provided localip must be a string!") 1378 1379 if type(localport) is not int: 1380 raise RepyArgumentError("Provided localport must be a int!") 1381 1382 1383 # Check the input arguments (sanity) 1384 if not _is_valid_ip_address(localip): 1385 raise RepyArgumentError("Provided localip is not valid! IP: '"+localip+"'") 1386 1387 if not _is_valid_network_port(localport): 1388 raise RepyArgumentError("Provided localport is not valid! Port: "+str(localport)) 1389 1390 1391 # Check the input arguments (permission) 1392 update_ip_cache() 1393 if not _ip_is_allowed(localip): 1394 raise ResourceForbiddenError("Provided localip is not allowed! IP: '"+localip+"'") 1395 1396 if not _is_allowed_localport("TCP", localport): 1397 raise ResourceForbiddenError("Provided localport is not allowed! Port: "+str(localport)) 1398 1399 # This is used to check if there is an existing connection with the same identity 1400 identity = ("TCP", localip, localport, None, None) 1401 1402 try: 1403 # Check if localip is on loopback 1404 on_loopback = _is_loopback_ipaddr(localip) 1405 # Get the socket 1406 sock = _get_tcp_socket(localip,localport) 1407 nanny.tattle_add_item('insockets',id(sock)) 1408 # Get the maximum number of outsockets 1409 max_outsockets = nanny.get_resource_limit("outsockets") 1410 # If we have restrictions, then we want to set the outsocket 1411 # limit 1412 if max_outsockets: 1413 # Set the backlog to be the maximum number of outsockets 1414 sock.listen(max_outsockets) 1415 else: 1416 sock.listen(5) 1417 1418 except Exception, e: 1419 1420 # Check if this an already in use error 1421 if _is_addr_in_use_exception(e): 1422 # Call _conn_cleanup_check to determine if this is because 1423 # the socket is being cleaned up or if it is actively being used 1424 # This will always raise DuplicateTupleError or 1425 # CleanupInProgressError or AlreadyListeningError 1426 _conn_cleanup_check(identity) 1427 1428 # Check if this is a binding error 1429 if _is_addr_unavailable_exception(e): 1430 # Call _conn_alreadyexists_check to determine if this is because 1431 # the connection is active or not 1432 _conn_alreadyexists_check(identity) 1433 # Unknown error... 1434 else: 1435 raise 1436 1437 server_sock = TCPServerSocket(sock, on_loopback) 1438 1439 # Return the TCPServerSocket 1440 return server_sock 1441 1442 1443# Private method to create a TCP socket and bind 1444# to a localip and localport. 1445# 1446def _get_tcp_socket(localip, localport): 1447 # Create the TCP socket 1448 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 1449 # Reuse the socket if it's "pseudo-availible" 1450 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 1451 1452 if localip and localport: 1453 try: 1454 s.bind((localip,localport)) 1455 except: # Raise the exception un-tainted 1456 # don't leak sockets 1457 s.close() 1458 raise 1459 return s 1460 1461 1462# Private method to create a UDP socket and bind 1463# to a localip and localport. 1464# 1465def _get_udp_socket(localip, localport): 1466 # Create the UDP socket 1467 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 1468 if localip and localport: 1469 try: 1470 s.bind((localip, localport)) 1471 except: 1472 # don't leak sockets 1473 s.close() 1474 raise 1475 return s 1476 1477 1478# Checks if the given real socket would block 1479def _check_socket_state(realsock, waitfor="rw", timeout=0.0): 1480 """ 1481 <Purpose> 1482 Checks if the given socket would block on a send() or recv(). 1483 In the case of a listening socket, read_will_block equates to 1484 accept_will_block. 1485 1486 <Arguments> 1487 realsock: 1488 A real socket.socket() object to check for. 1489 1490 waitfor: 1491 An optional specifier of what to wait for. "r" for read only, "w" for write only, 1492 and "rw" for read or write. E.g. if timeout is 10, and wait is "r", this will block 1493 for up to 10 seconds until read_will_block is false. If you specify "r", then 1494 write_will_block is always true, and if you specify "w" then read_will_block is 1495 always true. 1496 1497 timeout: 1498 An optional timeout to wait for the socket to be read or write ready. 1499 1500 <Returns> 1501 A tuple, (read_will_block, write_will_block). 1502 1503 <Exceptions> 1504 As with select.select(). Probably best to wrap this with _is_recoverable_network_exception 1505 and _is_terminated_connection_exception. Throws an exception if waitfor is not in ["r","w","rw"] 1506 """ 1507 # Check that waitfor is valid 1508 if waitfor not in ["rw","r","w"]: 1509 raise Exception, "Illegal waitfor argument!" 1510 1511 # Array to hold the socket 1512 sock_array = [realsock] 1513 1514 # Generate the read/write arrays 1515 read_array = [] 1516 if "r" in waitfor: 1517 read_array = sock_array 1518 1519 write_array = [] 1520 if "w" in waitfor: 1521 write_array = sock_array 1522 1523 # Call select() 1524 (readable, writeable, exception) = select.select(read_array,write_array,sock_array,timeout) 1525 1526 # If the socket is in the exception list, then assume its both read and writable 1527 if (realsock in exception): 1528 return (False, False) 1529 1530 # Return normally then 1531 return (realsock not in readable, realsock not in writeable) 1532 1533 1534##### Class Definitions 1535 1536# Public. We pass these to the users for communication purposes 1537class EmulatedSocket: 1538 """ 1539 This object is a wrapper around a tcp 1540 TCP socket. It allows for sending and 1541 recieving data, and closing the socket. 1542 1543 It operates in a strictly non-blocking mode, 1544 and uses Exceptions to indicate when an 1545 operation would result in blocking behavior. 1546 """ 1547 # Fields: 1548 # socket: This is a TCP Socket 1549 # 1550 # send_buffer_size: The size of the send buffer. We send less than 1551 # this to avoid a bug. 1552 # 1553 # on_loopback: true if the remote ip is a loopback address. 1554 # this is used for resource accounting. 1555 # sock_lock: Threading Lock on socket object used for 1556 # synchronization. 1557 __slots__ = ["socketobj", "send_buffer_size", "on_loopback", "sock_lock"] 1558 1559 1560 def __init__(self, sock, on_loopback): 1561 """ 1562 <Purpose> 1563 Initializes a EmulatedSocket object. 1564 1565 <Arguments> 1566 sock: A TCP Socket 1567 1568 on_loopback: True/False based on whether remote IP is 1569 on loopback oe not 1570 1571 <Exceptions> 1572 InteralRepyError is raised if there is no table entry for 1573 the socket. 1574 1575 <Returns> 1576 A EmulatedSocket object. 1577 """ 1578 # Store the parameters tuple 1579 self.socketobj = sock 1580 self.on_loopback = on_loopback 1581 self.sock_lock = threading.Lock() 1582 1583 # Store the socket send buffer size and set to non-blocking 1584 self.send_buffer_size = sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF) 1585 # locking should be unnecessary because there isn't another external 1586 # reference here yet 1587 sock.setblocking(0) 1588 1589 1590 def _close(self): 1591 """ 1592 <Purpose> 1593 Private close method. Called when socket lock is held. 1594 Does not perform any accounting / locking. Those should 1595 be done by the public methods. 1596 1597 <Arguments> 1598 None 1599 1600 <Side Effects> 1601 Closes the socket 1602 1603 <Returns> 1604 None 1605 """ 1606 # Clean up the socket 1607 _cleanup_socket(self) 1608 1609 # Replace the socket 1610 self.socketobj = None 1611 1612 1613 def close(self): 1614 """ 1615 <Purpose> 1616 Closes a socket. Pending remote recv() calls will return with the 1617 remaining information. Local recv / send calls will fail after this. 1618 1619 <Arguments> 1620 None 1621 1622 <Exceptions> 1623 None 1624 1625 <Side Effects> 1626 Pending local recv calls will either return or have an exception. 1627 1628 <Resource Consumption> 1629 If the connection is closed, no resources are consumed. This operation 1630 uses 64 bytes of netrecv, and 128 bytes of netsend. 1631 This call also stops consuming an outsocket. 1632 1633 <Returns> 1634 True if this is the first close call to this socket, False otherwise. 1635 """ 1636 # Get the socket lock 1637 socket_lock = self.sock_lock 1638 1639 if (self.socketobj == None): 1640 return False 1641 # Wait for resources 1642 if self.on_loopback: 1643 nanny.tattle_quantity('looprecv', 0) 1644 nanny.tattle_quantity('loopsend', 0) 1645 else: 1646 nanny.tattle_quantity('netrecv', 0) 1647 nanny.tattle_quantity('netsend', 0) 1648 1649 # Acquire the lock 1650 socket_lock.acquire() 1651 try: 1652 # Internal close 1653 self._close() 1654 1655 # Tattle the resources 1656 if self.on_loopback: 1657 nanny.tattle_quantity('looprecv',64) 1658 nanny.tattle_quantity('loopsend',128) 1659 else: 1660 nanny.tattle_quantity('netrecv',64) 1661 nanny.tattle_quantity('netsend',128) 1662 1663 # Done 1664 return True 1665 1666 finally: 1667 socket_lock.release() 1668 1669 1670 1671 def recv(self,bytes): 1672 """ 1673 <Purpose> 1674 Receives data from a socket. It may receive fewer bytes than 1675 requested. 1676 1677 <Arguments> 1678 bytes: 1679 The maximum number of bytes to read. 1680 1681 <Exceptions> 1682 SocketClosedLocal is raised if the socket was closed locally. 1683 SocketClosedRemote is raised if the socket was closed remotely. 1684 SocketWouldBlockError is raised if the socket operation would block. 1685 1686 <Side Effects> 1687 None. 1688 1689 <Resource Consumptions> 1690 This operations consumes 64 + amount of data in bytes 1691 wo…
Large files files are truncated, but you can click here to view the full file