PageRenderTime 59ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/emulcomm.py

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

Large files files are truncated, but you can click here to view the full file