Changeset 3360

Show
Ignore:
Timestamp:
01/13/10 15:09:26 (10 years ago)
Author:
jsamuel
Message:

Updated the namespace layer for the new repy api. Currently most functions hooked into it are fake (placeholders). The api doesn't exactly match the new repy api probably because I based it off of the current wiki page which is a little out of date. It hasn't been very thoroughly tested and the actual tests for it have not been updated yet.

Location:
seattle/branches/repy_v2/repy
Files:
3 added
1 modified

Legend:

Unmodified
Added
Removed
  • seattle/branches/repy_v2/repy/namespace.py

    r3248 r3360  
    7878     
    7979    FILE_OBJECT_WRAPPER_INFO 
    80     SOCKET_OBJECT_WRAPPER_INFO 
    8180    LOCK_OBJECT_WRAPPER_INFO 
     81    TCP_SOCKET_OBJECT_WRAPPER_INFO 
     82    TCP_SERVER_SOCKET_OBJECT_WRAPPER_INFO 
     83    UDP_SERVER_SOCKET_OBJECT_WRAPPER_INFO 
    8284    VIRTUAL_NAMESPACE_OBJECT_WRAPPER_INFO 
    8385     
     
    98100      of the corresponding NamespaceAPIFunctionWrapper instances. 
    99101       
    100     NamespaceViolationError 
     102    NamespaceInternalError 
    101103     
    102       This is the error that is raised from a wrapped function if any namespace 
    103       violation occurs other than invalid arguments. Invalid arguments raise 
    104       a TypeError to keep the behavior compatible with python's normal way of 
    105       handling numbers of or names of arguments. 
     104      If this error is raised anywhere (along with any other unexpected exceptions), 
     105      it should result in termination of the running program (see the except blocks 
     106      in NamespaceAPIFunctionWrapper.wrapped_function). 
    106107""" 
    107108 
     
    111112import thread 
    112113 
    113 import emulfile 
    114 import emultimer 
    115 import emulcomm 
    116 import emulmisc 
     114import fakeapi 
    117115 
    118116# Used to get SafeDict 
     
    140138  defined in the dictionary USERCONTEXT_WRAPPER_INFO. 
    141139  """ 
    142    
     140 
    143141  _init_namespace() 
    144    
     142 
    145143  for function_name in USERCONTEXT_WRAPPER_INFO: 
    146144    function_info = USERCONTEXT_WRAPPER_INFO[function_name] 
     
    177175# is empty, it means no methods can be called on a wrapped object of that type. 
    178176file_object_wrapped_functions_dict = {} 
    179 socket_object_wrapped_functions_dict = {} 
    180177lock_object_wrapped_functions_dict = {} 
     178tcp_socket_object_wrapped_functions_dict = {} 
     179tcp_server_socket_object_wrapped_functions_dict = {} 
     180udp_server_socket_object_wrapped_functions_dict = {} 
    181181virtual_namespace_object_wrapped_functions_dict = {} 
    182182 
     
    189189  """ 
    190190  objects_tuples = [(FILE_OBJECT_WRAPPER_INFO, file_object_wrapped_functions_dict), 
    191                     (SOCKET_OBJECT_WRAPPER_INFO, socket_object_wrapped_functions_dict), 
    192191                    (LOCK_OBJECT_WRAPPER_INFO, lock_object_wrapped_functions_dict), 
     192                    (TCP_SOCKET_OBJECT_WRAPPER_INFO, tcp_socket_object_wrapped_functions_dict), 
     193                    (TCP_SERVER_SOCKET_OBJECT_WRAPPER_INFO, tcp_server_socket_object_wrapped_functions_dict), 
     194                    (UDP_SERVER_SOCKET_OBJECT_WRAPPER_INFO, udp_server_socket_object_wrapped_functions_dict), 
    193195                    (VIRTUAL_NAMESPACE_OBJECT_WRAPPER_INFO, virtual_namespace_object_wrapped_functions_dict)] 
    194    
     196 
    195197  for description_dict, wrapped_func_dict in objects_tuples: 
    196198    for function_name in description_dict: 
    197199      function_info = description_dict[function_name] 
    198       wrapperobj = NamespaceAPIFunctionWrapper(function_info) 
     200      wrapperobj = NamespaceAPIFunctionWrapper(function_info, is_method=True) 
    199201      wrapped_func_dict[function_name] = wrapperobj.wrapped_function 
    200202 
     
    215217  NamespaceViolationError which will be seen by the offending user code. 
    216218  """ 
     219 
     220 
    217221 
    218222 
     
    230234      return True 
    231235  return False 
    232    
    233  
    234  
    235  
    236 def _require_string(obj): 
    237   if not _is_in(type(obj), [str, unicode]): 
    238     raise NamespaceRequirementError 
    239  
    240  
    241  
    242 def _require_integer(obj): 
    243   if not _is_in(type(obj), [int, long]): 
    244     raise NamespaceRequirementError 
    245  
    246  
    247  
    248 def _require_float(obj): 
    249   if type(obj) is not float: 
    250     raise NamespaceRequirementError 
    251  
    252  
    253  
    254 def _require_integer_or_float(obj): 
    255   if not _is_in(type(obj), [int, long, float]): 
    256     raise NamespaceRequirementError 
    257  
    258  
    259  
    260 def _require_bool(obj): 
    261   if type(obj) is not bool: 
    262     raise NamespaceRequirementError 
    263  
    264  
    265  
    266 def _require_bool_or_integer(obj): 
    267   if not _is_in(type(obj), [bool, int, long]): 
    268     raise NamespaceRequirementError 
    269  
    270  
    271  
    272 def _require_tuple(obj): 
    273   if type(obj) is not tuple: 
    274     raise NamespaceRequirementError 
    275  
    276  
    277  
    278 def _require_list(obj): 
    279   if type(obj) is not list: 
    280     raise NamespaceRequirementError 
    281  
    282  
    283  
    284 def _require_tuple_or_list(obj): 
    285   if not _is_in(type(obj), [tuple, list]): 
    286     raise NamespaceRequirementError 
    287  
    288  
    289  
    290 def _require_user_function(obj): 
    291   if not _is_in(type(obj), [types.FunctionType, types.LambdaType, types.MethodType]): 
    292     raise NamespaceRequirementError 
    293  
    294  
    295  
    296 def _require_list_of_strings(obj): 
    297   _require_list(obj) 
    298   for item in obj: 
    299     _require_string(item) 
    300  
    301  
    302 def _require_dict_or_safedict(obj): 
    303   if type(obj) is not dict and not isinstance(obj, safe.SafeDict): 
    304     raise NamespaceRequirementError 
    305  
    306 def _require_safedict(obj): 
    307   if not isinstance(obj, safe.SafeDict): 
    308     raise NamespaceRequirementError 
    309  
    310  
    311 ############################################################################## 
    312 # Functions that are used in the USERCONTEXT_WRAPPER_INFO to defined how each 
    313 # wrapper should be constructed. These raise NamespaceRequirementError if 
    314 # something is not considered acceptable. 
    315 ############################################################################## 
    316  
    317 def allow_all(*args, **kwargs): 
    318   pass 
    319  
    320  
    321  
    322 def allow_no_args(*args, **kwargs): 
    323   if len(args) != 0 or len(kwargs.keys()) != 0: 
    324     raise NamespaceRequirementError("No arguments allowed.") 
    325  
    326  
    327  
    328 def allow_args_single_integer_or_float(*args, **kwargs): 
    329   if len(args) != 1: 
    330     raise NamespaceRequirementError 
    331   if len(kwargs.keys()) != 0: 
    332     raise NamespaceRequirementError 
    333   _require_integer_or_float(args[0]) 
    334  
    335  
    336  
    337 def allow_return_none(retval): 
    338   if retval is not None: 
    339     raise NamespaceRequirementError 
    340  
    341  
    342  
    343 def allow_return_integer(retval): 
    344   _require_integer(retval) 
    345  
    346  
    347  
    348 def allow_return_float(retval): 
    349   _require_float(retval) 
    350  
    351  
    352  
    353 def allow_return_bool(retval): 
    354   _require_bool(retval) 
    355  
    356  
    357 # Armon: Boolean tuple with exactly 2 elements 
    358 def allow_return_two_bools_tuple(retval): 
    359   # First validate it is a boolean tuple 
    360   _require_tuple(retval) 
    361   for elem in retval: 
    362     _require_bool(elem) 
    363  
    364   # Check the size of the tuple is exactly 2 
    365   if len(retval) != 2: 
    366     raise NamespaceRequirementError 
    367  
    368  
    369  
    370 def allow_args_single_string(*args, **kwargs): 
    371   if len(args) != 1: 
    372     raise NamespaceRequirementError 
    373   if len(kwargs.keys()) != 0: 
    374     raise NamespaceRequirementError 
    375   _require_string(args[0]) 
    376  
    377  
    378  
    379 def allow_return_string(retval): 
    380   _require_string(retval) 
    381  
    382  
    383  
    384 def allow_return_gethostbyname_ex(retval): 
    385   _require_tuple(retval) 
    386   if len(retval) != 3: 
    387     raise NamespaceRequirementError 
    388  
    389   hostname, aliaslist, ipaddrlist = retval 
    390  
    391   _require_string(hostname) 
    392   _require_list_of_strings(aliaslist) 
    393   _require_list_of_strings(ipaddrlist) 
    394  
    395  
    396  
    397 def allow_args_recvmess_callback(remoteIP, remoteport, message, commhandle): 
    398   # The callback function should receive the following arguments: 
    399   # (remoteIP, remoteport, message, commhandle) 
    400    
    401   _require_string(remoteIP) 
    402   _require_integer(remoteport) 
    403   _require_string(message) 
    404    
    405   # There isn't much to check with the commhandle as once it is wrapped 
    406   # there isn't any way user code can interact with it. 
    407  
    408  
    409  
    410 def wrap_args_recvmess_callback(remoteIP, remoteport, message, commhandle): 
    411   """ 
    412   Wrap the commhandle passed into the callback function before the callback 
    413   function is actually called. 
    414   """ 
    415   # The callback function should receive the following arguments: 
    416   # (remoteIP, remoteport, message, commhandle) 
    417  
    418   wrapped_commhandle = wrap_commhandle_obj(commhandle) 
    419    
    420   args = (remoteIP, remoteport, message, wrapped_commhandle) 
    421   kwargs = {} 
    422   return args, kwargs 
    423  
    424  
    425  
    426 def allow_args_recvmess(localip, localport, function): 
    427   _require_string(localip) 
    428   _require_integer(localport) 
    429   _require_user_function(function) 
    430  
    431  
    432  
    433 def wrap_args_recvmess(localip, localport, function): 
    434   """ 
    435   Wrap the callback function passed from user code to privileged code. This is 
    436   done so that we can intercept calls to the callback function and check and 
    437   wrap arguments passed into the untrusted callback function. 
    438   """ 
    439  
    440   function_info = {'target_func' : function, 
    441                    'arg_checking_func' : allow_args_recvmess_callback, 
    442                    'arg_wrapping_func' : wrap_args_recvmess_callback, 
    443                    'return_checking_func' : allow_all} 
    444    
    445   wrapperobj = NamespaceAPIFunctionWrapper(function_info) 
    446    
    447   args = (localip, localport, wrapperobj.wrapped_function) 
    448   kwargs = {} 
    449   return args, kwargs 
    450  
    451  
    452  
    453 def allow_args_sendmess(desthost, destport, message, localip=None, localport=None): 
    454  
    455   _require_string(desthost) 
    456   _require_integer(destport) 
    457   _require_string(message) 
    458  
    459   # The user must provide either both or neither of localip and localport, 
    460   # but we're going to let the actual sendmess worry about that. 
    461   if localip is not None: 
    462     _require_string(localip) 
    463   if localport is not None: 
    464     _require_integer(localport) 
    465  
    466  
    467  
    468 def allow_args_openconn(desthost, destport, localip=None, localport=0, timeout=5): 
    469   # TODO: the wiki:RepyLibrary gives localport=0 as the default for this function, 
    470   # slightly different than the localport=None it gives for sendmess(). This 
    471   # should either be verified as intentional or made the same. 
    472  
    473   _require_string(desthost) 
    474   _require_integer(destport) 
    475  
    476   # The user must provide either both or neither of localip and localport, 
    477   # but we're going to let the actual sendmess worry about that. 
    478   if localip is not None: 
    479     _require_string(localip) 
    480   # We accept a localport of None to mean the same thing as 0. 
    481   if localport is not None: 
    482     _require_integer(localport) 
    483  
    484   _require_integer_or_float(timeout) 
    485  
    486  
    487  
    488 def allow_args_waitforconn_callback(remoteip, remoteport, socketlikeobj, thiscommhandle, listencommhandle): 
    489   # The callback function should receive the following arguments: 
    490   # (remoteip, remoteport, socketlikeobj, thiscommhandle, listencommhandle) 
    491    
    492   _require_string(remoteip) 
    493   _require_integer(remoteport) 
    494   _require_emulated_socket(socketlikeobj) 
    495   # There isn't much to check with the commhandles as once they are wrapped 
    496   # there isn't any way user code can interact with them. 
    497    
    498  
    499  
    500 def wrap_args_waitforconn_callback(remoteip, remoteport, socketlikeobj, thiscommhandle, listencommhandle): 
    501   """ 
    502   Wrap the socketlikeobj, thiscommhandle, listencommhandle passed into the 
    503   callback function before the callback function is actually called. 
    504   """ 
    505   # The callback function should receive the following arguments: 
    506   # (remoteip, remoteport, socketlikeobj, thiscommhandle, listencommhandle) 
    507  
    508   wrapped_socketlikeobj = wrap_socket_obj(socketlikeobj) 
    509   wrapped_thiscommhandle = wrap_commhandle_obj(thiscommhandle) 
    510   wrapped_listencommhandle = wrap_commhandle_obj(listencommhandle) 
    511    
    512   args = (remoteip, remoteport, wrapped_socketlikeobj, wrapped_thiscommhandle, 
    513           wrapped_listencommhandle) 
    514   kwargs = {} 
    515   return args, kwargs 
    516  
    517  
    518  
    519 def allow_args_waitforconn(localip, localport, function): 
    520   _require_string(localip) 
    521   _require_integer(localport) 
    522   _require_user_function(function) 
    523  
    524  
    525  
    526 def wrap_args_waitforconn(localip, localport, function): 
    527   """ 
    528   Wrap the callback function passed from user code to privileged code. This is 
    529   done so that we can intercept calls to the callback function and check and 
    530   wrap arguments passed into the untrusted callback function. 
    531   """ 
    532  
    533   function_info = {'target_func' : function, 
    534                    'arg_checking_func' : allow_args_waitforconn_callback, 
    535                    'arg_wrapping_func' : wrap_args_waitforconn_callback, 
    536                    'return_checking_func' : allow_all} 
    537    
    538   wrapperobj = NamespaceAPIFunctionWrapper(function_info) 
    539    
    540   args = (localip, localport, wrapperobj.wrapped_function) 
    541   kwargs = {} 
    542   return args, kwargs 
    543  
    544  
    545  
    546  
    547  
    548  
    549  
    550 def allow_args_stopcomm(wrapped_commhandle): 
    551   try: 
    552     if wrapped_commhandle._wrapped__type_name != "commhandle": 
    553       raise NamespaceRequirementError 
    554   except AttributeError: 
    555     raise NamespaceRequirementError 
    556  
    557  
    558  
    559  
    560 def allow_args_open(filename, mode='r'): 
    561   _require_string(filename) 
    562   _require_string(mode) 
    563  
    564  
    565  
    566 def allow_return_list_of_strings(retval): 
    567   _require_list_of_strings(retval) 
    568  
    569  
    570  
    571 def allow_args_settimer(waittime, function, args): 
    572   _require_integer_or_float(waittime) 
    573   _require_user_function(function) 
    574   _require_tuple_or_list(args) 
    575  
    576  
    577  
    578 def allow_args_canceltimer(wrapped_timerhandle): 
    579   try: 
    580     if wrapped_timerhandle._wrapped__type_name != "timerhandle": 
    581       raise NamespaceRequirementError 
    582   except AttributeError: 
    583     raise NamespaceRequirementError 
    584  
    585  
    586 def allow_args_virtual_namespace(code, name="<string>"): 
    587   _require_string(code) 
    588   _require_string(name) 
    589  
    590  
    591 def wrap_commhandle_obj(commhandle): 
    592   # There are no attributes to be accessed on commhandles. 
    593   return NamespaceObjectWrapper("commhandle", commhandle, []) 
    594  
    595  
    596  
    597 def wrap_timerhandle_obj(timerhandle): 
    598   # There are no attributes to be accessed on timerhandles. 
    599   return NamespaceObjectWrapper("timerhandle", timerhandle, []) 
    600  
    601  
    602  
    603 def wrap_socket_obj(socketobj): 
    604   _require_emulated_socket(socketobj) 
    605   return NamespaceObjectWrapper("socket", socketobj, socket_object_wrapped_functions_dict) 
    606  
    607  
    608  
    609 def wrap_lock_obj(lockobj): 
    610   _require_lock_object(lockobj) 
    611   return NamespaceObjectWrapper("lock", lockobj, lock_object_wrapped_functions_dict) 
    612  
    613  
    614  
    615 def wrap_file_obj(fileobj): 
    616   _require_emulated_file(fileobj) 
    617   return NamespaceObjectWrapper("file", fileobj, file_object_wrapped_functions_dict) 
    618  
    619 def wrap_virtual_namespace_obj(virt): 
    620   _require_virtual_namespace_object(virt) 
    621   return NamespaceObjectWrapper("VirtualNamespace", virt, virtual_namespace_object_wrapped_functions_dict) 
    622  
    623  
    624 def unwrap_single_arg(*args, **kwargs): 
    625   unwrapped_args = (args[0]._wrapped__object,) 
    626   unwrapped_kwargs = {} 
    627   return unwrapped_args, unwrapped_kwargs 
    628  
    629  
    630236 
    631237############################################################################## 
     
    634240# wrap_builtin_functions(). 
    635241############################################################################## 
     242 
     243class RepyError(Exception): 
     244  """Base repy exception.""" 
     245 
     246 
     247 
     248 
     249 
     250class RepyArgumentError(RepyError): 
     251  """Anything wrong with an argument.""" 
     252 
     253 
     254 
     255 
     256 
     257class BaseProcessor(object): 
     258  """Base type for ValueProcess and ObjectProcessor.""" 
     259 
     260 
     261 
     262 
     263 
     264class ValueProcessor(BaseProcessor): 
     265  """ 
     266  This is for simple/builtin types and combinations of them. Basically, 
     267  anything that needs to be copied when used as an argument or return 
     268  value and doesn't need to be wrapped or unwrapped as it passes through 
     269  the namespace layer. 
     270  """ 
     271 
     272  def check(self): 
     273    raise NotImplementedError 
     274 
     275  def copy(self, val): 
     276    return _copy(val) 
     277 
     278 
     279 
     280class ObjectProcessor(BaseProcessor): 
     281  """ 
     282  This is for for anything that needs to be wrapped or unwrapped (not copied) 
     283  as it passes through the namespace layer. 
     284  """ 
     285 
     286  def check(self): 
     287    raise NotImplementedError 
     288 
     289  def wrap(self, val): 
     290    raise NotImplementedError 
     291 
     292  def unwrap(self, val): 
     293    return val._wrapped__object 
     294 
     295 
     296 
     297 
     298 
     299class Str(ValueProcessor): 
     300  """Allows str or unicode.""" 
     301 
     302  def __init__(self, maxlen=None, minlen=None): 
     303    self.maxlen = maxlen 
     304    self.minlen = minlen 
     305 
     306 
     307 
     308  def check(self, val): 
     309    if not _is_in(type(val), [str, unicode]): 
     310      raise RepyArgumentError("Invalid type %s" % type(val)) 
     311 
     312    if self.maxlen is not None: 
     313      if len(val) > self.maxlen: 
     314        raise RepyArgumentError("Max string length is %s" % self.maxlen) 
     315 
     316    if self.minlen is not None: 
     317      if len(val) < self.minlen: 
     318        raise RepyArgumentError("Min string length is %s" % self.minlen) 
     319 
     320 
     321 
     322 
     323 
     324class Int(ValueProcessor): 
     325  """Allows int or long.""" 
     326 
     327  def __init__(self, min=0): 
     328    self.min = min 
     329 
     330 
     331 
     332  def check(self, val): 
     333    if not _is_in(type(val), [int, long]): 
     334      raise RepyArgumentError("Invalid type %s" % type(val)) 
     335 
     336    if val < self.min: 
     337      raise RepyArgumentError("Min value is %s." % self.min) 
     338 
     339 
     340 
     341 
     342 
     343class StrOrInt(ValueProcessor): 
     344  """Allows a string or int. This doesn't enforce max/min/length limits on the 
     345  strings and ints.""" 
     346 
     347  def check(self, val): 
     348    if not _is_in(type(val), [int, long, str, unicode]): 
     349      raise RepyArgumentError("Invalid type %s" % type(val)) 
     350 
     351 
     352 
     353 
     354 
     355class Float(ValueProcessor): 
     356  """Allows float, int, or long.""" 
     357 
     358  def __init__(self, allow_neg=False): 
     359    self.allow_neg = allow_neg 
     360 
     361 
     362 
     363  def check(self, val): 
     364    if not _is_in(type(val), [int, long, float]): 
     365      raise RepyArgumentError("Invalid type %s" % type(val)) 
     366 
     367    if not self.allow_neg: 
     368      if val < 0: 
     369        raise RepyArgumentError("Must be non-negative.") 
     370 
     371 
     372 
     373 
     374 
     375class Bool(ValueProcessor): 
     376  """Allows bool.""" 
     377 
     378  def check(self, val): 
     379    if type(val) is not bool: 
     380      raise RepyArgumentError("Invalid type %s" % type(val)) 
     381 
     382 
     383 
     384 
     385 
     386class ListOfStr(ValueProcessor): 
     387  """Allows lists of strings. This doesn't enforce max/min/length limits on the 
     388  strings and ints.""" 
     389 
     390  def check(self, val): 
     391    if not type(val) is list: 
     392      raise RepyArgumentError("Invalid type %s" % type(val)) 
     393 
     394    for item in val: 
     395      Str().check(item) 
     396 
     397 
     398 
     399 
     400 
     401class DictOfStrOrInt(ValueProcessor): 
     402  """ 
     403  Allows a tuple that contains dictionaries that only contain string keys 
     404  and str or int values. This doesn't enforce max/min/length limits on the 
     405  strings and ints. 
     406  """ 
     407 
     408  def check(self, val): 
     409    if not type(val) is dict: 
     410      raise RepyArgumentError("Invalid type %s" % type(val)) 
     411 
     412    for key, value in val.items(): 
     413      Str().check(key) 
     414      StrOrInt().check(value) 
     415 
     416 
     417 
     418 
     419 
     420class Func(ValueProcessor): 
     421  """Allows a user-defined function object.""" 
     422 
     423  def check(self, val): 
     424    if not _is_in(type(val), [types.FunctionType, types.LambdaType, types.MethodType]): 
     425      raise RepyArgumentError("Invalid type %s" % type(val)) 
     426 
     427 
     428 
     429 
     430 
     431class NonCopiedVarArgs(ValueProcessor): 
     432  """Allows any number of arguments. This must be the last arg listed. """ 
     433 
     434  def check(self, val): 
     435    pass 
     436 
     437 
     438 
     439  def copy(self, val): 
     440    return val 
     441 
     442 
     443 
     444 
     445 
     446class File(ObjectProcessor): 
     447  """Allows File objects.""" 
     448 
     449  def check(self, val): 
     450    if not isinstance(val, fakeapi.FileObj): 
     451      raise RepyArgumentError("Invalid type %s" % type(val)) 
     452 
     453 
     454 
     455  def wrap(self, val): 
     456    return NamespaceObjectWrapper("file", val, file_object_wrapped_functions_dict) 
     457 
     458 
     459 
     460 
     461 
     462class Lock(ObjectProcessor): 
     463  """Allows Lock objects.""" 
     464 
     465  def check(self, val): 
     466    # TODO: it's probably a real lock. 
     467    if not isinstance(val, thread.LockType): 
     468      raise RepyArgumentError("Invalid type %s" % type(val)) 
     469 
     470 
     471 
     472  def wrap(self, val): 
     473    return NamespaceObjectWrapper("lock", val, lock_object_wrapped_functions_dict) 
     474 
     475 
     476 
     477 
     478 
     479class UDPServerSocket(ObjectProcessor): 
     480  """Allows UDPServerSocket objects.""" 
     481 
     482  def check(self, val): 
     483    if not isinstance(val, fakeapi.UDPServerSocketObj): 
     484      raise RepyArgumentError("Invalid type %s" % type(val)) 
     485 
     486 
     487 
     488  def wrap(self, val): 
     489    return NamespaceObjectWrapper("socket", val, udp_server_socket_object_wrapped_functions_dict) 
     490 
     491 
     492 
     493 
     494 
     495class TCPServerSocket(ObjectProcessor): 
     496  """Allows TCPServerSocket objects.""" 
     497 
     498  def check(self, val): 
     499    if not isinstance(val, fakeapi.TCPServerSocketObj): 
     500      raise RepyArgumentError("Invalid type %s" % type(val)) 
     501 
     502 
     503 
     504  def wrap(self, val): 
     505    return NamespaceObjectWrapper("socket", val, tcp_server_socket_object_wrapped_functions_dict) 
     506 
     507 
     508 
     509 
     510 
     511class TCPSocket(ObjectProcessor): 
     512  """Allows TCPSocket objects.""" 
     513 
     514  def check(self, val): 
     515    if not isinstance(val, fakeapi.TCPSocketObj): 
     516      raise RepyArgumentError("Invalid type %s" % type(val)) 
     517 
     518 
     519 
     520  def wrap(self, val): 
     521    return NamespaceObjectWrapper("socket", val, tcp_socket_object_wrapped_functions_dict) 
     522 
     523 
     524 
     525 
     526 
     527class VirtualNamespace(ObjectProcessor): 
     528  """Allows VirtualNamespace objects.""" 
     529 
     530  def check(self, val): 
     531    if not isinstance(val, virtual_namespace.VirtualNamespace): 
     532      raise RepyArgumentError("Invalid type %s" % type(val)) 
     533 
     534 
     535 
     536  def wrap(self, val): 
     537    return NamespaceObjectWrapper("VirtualNamespace", val, 
     538                                  virtual_namespace_object_wrapped_functions_dict) 
     539 
     540 
     541 
     542 
     543 
     544class SafeDict(ObjectProcessor): 
     545  """Allows SafeDict objects.""" 
     546 
     547  # TODO: provide a copy function that won't actually copy so that 
     548  # references are maintained. 
     549  def check(self, val): 
     550    if not isinstance(val, safe.SafeDict): 
     551      raise RepyArgumentError("Invalid type %s" % type(val)) 
     552 
     553 
     554 
     555 
     556 
     557class DictOrSafeDict(ObjectProcessor): 
     558  """Allows SafeDict objects or regular dict objects.""" 
     559 
     560  # TODO: provide a copy function that won't actually copy so that 
     561  # references are maintained. 
     562  def check(self, val): 
     563    if type(val) is not dict: 
     564      SafeDict(val).check() 
     565 
     566 
     567 
     568 
    636569 
    637570# These are the functions in the user's name space excluding the builtins we 
     
    644577# defined here will be wrapped and made available to untrusted user code. 
    645578USERCONTEXT_WRAPPER_INFO = { 
    646   # emulated open function 
    647   'open' : 
    648       {'target_func' : emulfile.emulated_open, 
    649        'arg_checking_func' : allow_args_open, 
    650        # Even though the checking function is allow_all, the wrapping function 
    651        # does check the type. 
    652        'return_checking_func' : allow_all, 
    653        'return_wrapping_func' : wrap_file_obj}, 
    654  
    655   # emulated file object 
    656   'file' : 
    657       {'target_func' : emulfile.emulated_open, 
    658        'arg_checking_func' : allow_args_open, 
    659        # Even though the checking function is allow_all, the wrapping function 
    660        # does check the type. 
    661        'return_checking_func' : allow_all, 
    662        'return_wrapping_func' : wrap_file_obj}, 
    663  
    664   # List the files in the sandboxed program's area 
    665   'listdir' : 
    666       {'target_func' : emulfile.listdir, 
    667        'arg_checking_func' : allow_no_args, 
    668        'return_checking_func' : allow_return_list_of_strings}, 
    669  
    670   # remove a file in the sandboxed program's area 
     579  'gethostbyname' : 
     580      {'func' : fakeapi.gethostbyname, 
     581       'args' : [Str()], 
     582       'return' : Str()}, 
     583  'getmyip' : 
     584      {'func' : fakeapi.getmyip, 
     585       'args' : [], 
     586       'return' : Str()}, 
     587  'sendmessage' : 
     588      {'func' : fakeapi.sendmessage, 
     589       'args' : [Str(), Int(), Str(), Str(), Int()], 
     590       'return' : Int()}, 
     591  'listenformessage' : 
     592      {'func' : fakeapi.listenformessage, 
     593       'args' : [Str(), Int()], 
     594       'return' : UDPServerSocket()}, 
     595  'openconnection' : 
     596      {'func' : fakeapi.openconnection, 
     597       'args' : [Str(), Int(), Str(), Int(), Int()], 
     598       'return' : TCPSocket()}, 
     599  'listenforconnection' : 
     600      {'func' : fakeapi.listenforconnection, 
     601       'args' : [Str(), Int()], 
     602       'return' : TCPServerSocket()}, 
     603  'openfile' : 
     604      {'func' : fakeapi.openfile, 
     605       'args' : [Str(maxlen=120), Bool()], 
     606       'return' : File()}, 
     607  'listfiles' : 
     608      {'func' : fakeapi.listfiles, 
     609       'args' : [], 
     610       'return' : ListOfStr()}, 
    671611  'removefile' : 
    672       {'target_func' : emulfile.removefile, 
    673        'arg_checking_func' : allow_args_single_string, 
    674        'return_checking_func' : allow_return_none}, 
    675  
    676   # provides an external IP 
    677   'getmyip' : 
    678       {'target_func' : emulcomm.getmyip, 
    679        'arg_checking_func' : allow_no_args, 
    680        'return_checking_func' : allow_return_string}, 
    681  
    682   # same as socket method 
    683   'gethostbyname_ex' : 
    684       {'target_func' : emulcomm.gethostbyname_ex, 
    685        'arg_checking_func' : allow_args_single_string, 
    686        'return_checking_func' : allow_return_gethostbyname_ex}, 
    687  
    688   # message receive (UDP) 
    689   'recvmess' : 
    690       {'target_func' : emulcomm.recvmess, 
    691        'arg_checking_func' : allow_args_recvmess, 
    692        'arg_wrapping_func' : wrap_args_recvmess, 
    693        'return_checking_func' : allow_all, 
    694        'return_wrapping_func' : wrap_commhandle_obj}, 
    695  
    696   # message sending (UDP) 
    697   'sendmess' : 
    698       {'target_func' : emulcomm.sendmess, 
    699        'arg_checking_func' : allow_args_sendmess, 
    700        'return_checking_func' : allow_return_integer}, 
    701  
    702   # reliable comm channel (TCP) 
    703   'openconn' : 
    704       {'target_func' : emulcomm.openconn, 
    705        'arg_checking_func' : allow_args_openconn, 
    706        # Even though the checking function is allow_all, the wrapping function 
    707        # does check the type. 
    708        'return_checking_func' : allow_all, 
    709        'return_wrapping_func' : wrap_socket_obj}, 
    710  
    711   # reliable comm listen (TCP) 
    712   'waitforconn' : 
    713       {'target_func' : emulcomm.waitforconn, 
    714        'arg_checking_func' : allow_args_waitforconn, 
    715        'arg_wrapping_func' : wrap_args_waitforconn, 
    716        # There isn't much to check in terms of the return value as the commhandle 
    717        # wrapper doesn't let anything be done to the wrapped object. 
    718        'return_checking_func' : allow_all, 
    719        'return_wrapping_func' : wrap_commhandle_obj}, 
    720  
    721   # stop receiving (TCP/UDP) 
    722   'stopcomm' : 
    723       {'target_func' : emulcomm.stopcomm, 
    724        'arg_checking_func' : allow_args_stopcomm, 
    725        'arg_unwrapping_func' : unwrap_single_arg, 
    726        'return_checking_func' : allow_return_bool}, 
    727  
    728   # sets a timer 
    729   'settimer' : 
    730       {'target_func' : emultimer.settimer, 
    731        'arg_checking_func' : allow_args_settimer, 
    732        # There isn't much to check in terms of the return value as the timerhandle 
    733        # wrapper doesn't let anything be done to the wrapped object. 
    734        'return_checking_func' : allow_all, 
    735        'return_wrapping_func' : wrap_timerhandle_obj}, 
    736  
    737   # stops a timer if it hasn't fired 
    738   'canceltimer' : 
    739       {'target_func' : emultimer.canceltimer, 
    740        'arg_checking_func' : allow_args_canceltimer, 
    741        'arg_unwrapping_func' : unwrap_single_arg, 
    742        'return_checking_func' : allow_return_bool}, 
    743  
    744   # blocks the thread for some time 
     612      {'func' : fakeapi.removefile, 
     613       'args' : [Str(maxlen=120)], 
     614       'return' : None}, 
     615  'exitall' : 
     616      {'func' : fakeapi.exitall, 
     617       'args' : [], 
     618       'return' : None}, 
     619  'createlock' : 
     620      {'func' : fakeapi.createlock, 
     621       'args' : [], 
     622       'return' : Lock()}, 
     623  'getruntime' : 
     624      {'func' : fakeapi.getruntime, 
     625       'args' : [], 
     626       'return' : Float()}, 
     627  'randombytes' : 
     628      {'func' : fakeapi.randombytes, 
     629       'args' : [], 
     630       'return' : Str(maxlen=1024, minlen=1024)}, 
     631  'createthread' : 
     632      {'func' : fakeapi.createthread, 
     633       'args' : [Func(), NonCopiedVarArgs()], 
     634       'return' : None}, 
    745635  'sleep' : 
    746       {'target_func' : emultimer.sleep, 
    747        'arg_checking_func' : allow_args_single_integer_or_float, 
    748        'return_checking_func' : allow_return_none}, 
    749  
    750   # same as random.random() 
    751   'randomfloat' : 
    752       {'target_func' : emulmisc.randomfloat, 
    753        'arg_checking_func' : allow_no_args, 
    754        'return_checking_func' : allow_return_float}, 
    755  
    756   # amount of time the program has run 
    757   'getruntime' : 
    758       {'target_func' : emulmisc.getruntime, 
    759        'arg_checking_func' : allow_no_args, 
    760        'return_checking_func' : allow_return_float}, 
    761  
    762   # acquire a lock object 
    763   'getlock' : 
    764       {'target_func' : emulmisc.getlock, 
    765        'arg_checking_func' : allow_no_args, 
    766        # Even though the checking function is allow_all, the wrapping function 
    767        # does check the type. 
    768        'return_checking_func' : allow_all, 
    769        'return_wrapping_func' : wrap_lock_obj}, 
    770  
    771   # Stops executing the sandboxed program 
    772   'exitall' : 
    773       {'target_func' : emulmisc.exitall, 
    774        'arg_checking_func' : allow_no_args, 
    775        'return_checking_func' : allow_return_none}, 
    776  
    777   # Provides an unique identifier for the current thread 
    778   'get_thread_name' : 
    779       {'target_func' : emulmisc.get_thread_name, 
    780        'arg_checking_func' : allow_no_args, 
    781        'return_checking_func' : allow_return_string}, 
    782  
    783   # Provides a safe execution environment for arbitrary code 
    784   'VirtualNamespace' : 
    785       {'target_func' : virtual_namespace.get_VirtualNamespace, 
    786        'arg_checking_func' : allow_args_virtual_namespace, 
    787        'return_checking_func' : allow_all, 
    788        'return_wrapping_func' : wrap_virtual_namespace_obj} 
     636      {'func' : fakeapi.sleep, 
     637       'args' : [Float()], 
     638       'return' : None}, 
     639  'getthreadname' : 
     640      {'func' : fakeapi.getthreadname, 
     641       'args' : [], 
     642       'return' : Str()}, 
     643  'createvirtualnamespace' : 
     644      {'func' : virtual_namespace.get_VirtualNamespace, 
     645       'args' : [Str(), Str()], 
     646       'return' : VirtualNamespace()}, 
     647  'getresources' : 
     648      {'func' : fakeapi.getresources, 
     649       'args' : [], 
     650       'return' : (DictOfStrOrInt(), DictOfStrOrInt())}, 
    789651} 
    790  
    791  
    792  
    793  
    794  
    795 def _require_emulated_file(fileobj): 
    796   if not isinstance(fileobj, emulfile.emulated_file): 
    797     raise NamespaceRequirementError("Expected emulated_file, received " + str(fileobj)) 
    798  
    799  
    800  
    801 def allow_args_emulated_file(file): 
    802   _require_emulated_file(file) 
    803  
    804  
    805  
    806 def allow_args_emulated_file_and_optional_integer(*args, **kwargs): 
    807   # First arg is self, which is required. 
    808   if len(args) not in [1, 2]: 
    809     raise NamespaceRequirementError 
    810    
    811   if len(kwargs.keys()) != 0: 
    812     raise NamespaceRequirementError 
    813    
    814   _require_emulated_file(args[0]) 
    815    
    816   # If the user did provide the optional single integer, make sure it's an int. 
    817   if len(args) == 2: 
    818     _require_integer(args[1]) 
    819  
    820  
    821  
    822 def allow_args_emulated_file_seek(fileobj, offset, whence=0): 
    823   _require_emulated_file(fileobj) 
    824   # Note: offset can be negative. Resist the urge to require it to be 
    825   # non-negative. 
    826   _require_integer(offset) 
    827   _require_integer(whence) 
    828  
    829  
    830  
    831 def allow_args_emulated_file_write(fileobj, data): 
    832   _require_emulated_file(fileobj) 
    833   _require_string(data) 
    834  
    835  
    836  
    837 def allow_args_emulated_file_writelines(fileobj, lines): 
    838   _require_emulated_file(fileobj) 
    839   _require_list_of_strings(lines) 
    840    
    841    
    842652 
    843653FILE_OBJECT_WRAPPER_INFO = { 
    844654  'close' : 
    845       {'target_func' : emulfile.emulated_file.close, 
    846        'arg_checking_func' : allow_args_emulated_file, 
    847        'return_checking_func' : allow_return_none}, 
    848         
    849   'flush' : 
    850       {'target_func' : emulfile.emulated_file.flush, 
    851        'arg_checking_func' : allow_args_emulated_file, 
    852        'return_checking_func' : allow_return_none}, 
    853  
    854   'next' : 
    855       {'target_func' : emulfile.emulated_file.next, 
    856        'arg_checking_func' : allow_args_emulated_file, 
    857        'return_checking_func' : allow_return_string}, 
    858  
    859   'read' : 
    860       {'target_func' : emulfile.emulated_file.read, 
    861        'arg_checking_func' : allow_args_emulated_file_and_optional_integer, 
    862        'return_checking_func' : allow_return_string}, 
    863  
    864   'readline' : 
    865       {'target_func' : emulfile.emulated_file.readline, 
    866        'arg_checking_func' : allow_args_emulated_file_and_optional_integer, 
    867        'return_checking_func' : allow_return_string}, 
    868  
    869   'readlines' : 
    870       {'target_func' : emulfile.emulated_file.readlines, 
    871        'arg_checking_func' : allow_args_emulated_file_and_optional_integer, 
    872        'return_checking_func' : allow_return_list_of_strings}, 
    873         
    874   'seek' : 
    875       {'target_func' : emulfile.emulated_file.seek, 
    876        'arg_checking_func' : allow_args_emulated_file_seek, 
    877        'return_checking_func' : allow_return_none}, 
    878         
    879   'write' : 
    880       {'target_func' : emulfile.emulated_file.write, 
    881        'arg_checking_func' : allow_args_emulated_file_write, 
    882        'return_checking_func' : allow_return_none}, 
    883         
    884   'writelines' : 
    885       {'target_func' : emulfile.emulated_file.writelines, 
    886        'arg_checking_func' : allow_args_emulated_file_writelines, 
    887        'return_checking_func' : allow_return_none}, 
     655      {'func' : fakeapi.FileObj.close, 
     656       'args' : [], 
     657       'return' : None}, 
     658  'readat' : 
     659      {'func' : fakeapi.FileObj.readat, 
     660       'args' : [Int(), Int()], 
     661       'return' : Str()}, 
     662  'writeat' : 
     663      {'func' : fakeapi.FileObj.writeat, 
     664       'args' : [Str(), Int()], 
     665       'return' : None}, 
    888666} 
    889667 
    890  
    891  
    892  
    893  
    894  
    895 def _require_emulated_socket(socket): 
    896   if not isinstance(socket, emulcomm.emulated_socket): 
    897     raise NamespaceRequirementError("Expected emulated_socket, received " + str(socket)) 
    898  
    899  
    900  
    901 def allow_args_emulated_socket(socket): 
    902   _require_emulated_socket(socket) 
    903  
    904  
    905  
    906 def allow_args_emulated_socket_send(socket, data): 
    907   _require_emulated_socket(socket) 
    908   _require_string(data) 
    909  
    910  
    911  
    912 def allow_args_emulated_socket_recv(socket, bytes): 
    913   _require_emulated_socket(socket) 
    914   _require_integer(bytes) 
    915  
    916  
    917  
    918 SOCKET_OBJECT_WRAPPER_INFO = { 
     668TCP_SOCKET_OBJECT_WRAPPER_INFO = { 
    919669  'close' : 
    920       {'target_func' : emulcomm.emulated_socket.close, 
    921        'arg_checking_func' : allow_args_emulated_socket, 
    922        'return_checking_func' : allow_return_bool}, 
    923  
     670      {'func' : fakeapi.TCPSocketObj.close, 
     671       'args' : [], 
     672       'return' : Bool()}, 
    924673  'recv' : 
    925       {'target_func' : emulcomm.emulated_socket.recv, 
    926        'arg_checking_func' : allow_args_emulated_socket_recv, 
    927        'return_checking_func' : allow_return_string}, 
    928         
     674      {'func' : fakeapi.TCPSocketObj.recv, 
     675       'args' : [Int(min=1)], 
     676       'return' : Str()}, 
    929677  'send' : 
    930       {'target_func' : emulcomm.emulated_socket.send, 
    931        'arg_checking_func' : allow_args_emulated_socket_send, 
    932        'return_checking_func' : allow_return_integer}, 
    933    
    934   # Armon: Add the willblock() call. Takes no args, and returns a bool tuple with 2 entries. 
    935   'willblock' : 
    936       {'target_func' : emulcomm.emulated_socket.willblock, 
    937        'arg_checking_func' : allow_args_emulated_socket, 
    938        'return_checking_func' : allow_return_two_bools_tuple}, 
    939  
     678      {'func' : fakeapi.TCPSocketObj.send, 
     679       'args' : [Str()], 
     680       'return' : Int(min=1)}, 
    940681} 
    941682 
    942  
    943  
    944  
    945  
    946 def _require_lock_object(lockobj): 
    947   # The type(lockobj) is thread.lock, but there is no such thing. So, we use 
    948   # 'isinstance()' here instead of 'is'. 
    949   if not isinstance(lockobj, thread.LockType): 
    950     raise NamespaceRequirementError 
    951  
    952  
    953  
    954 def allow_args_lock_acquire(lock, blocking=1): 
    955   _require_lock_object(lock) 
    956   _require_bool_or_integer(blocking) 
    957  
    958  
    959  
    960 def allow_args_lock_release(lock): 
    961   _require_lock_object(lock) 
    962  
    963  
     683TCP_SERVER_SOCKET_OBJECT_WRAPPER_INFO = { 
     684  'close' : 
     685      {'func' : fakeapi.TCPServerSocketObj.close, 
     686       'args' : [], 
     687       'return' : Bool()}, 
     688  'getconnection' : 
     689      {'func' : fakeapi.TCPServerSocketObj.getconnection, 
     690       'args' : [], 
     691       'return' : (Str(), Int(), TCPSocket())}, 
     692} 
     693 
     694UDP_SERVER_SOCKET_OBJECT_WRAPPER_INFO = { 
     695  'close' : 
     696      {'func' : fakeapi.UDPServerSocketObj.close, 
     697       'args' : [], 
     698       'return' : Bool()}, 
     699  'getmessage' : 
     700      {'func' : fakeapi.UDPServerSocketObj.getmessage, 
     701       'args' : [], 
     702       'return' : (Str(), Int(), Str())}, 
     703} 
    964704 
    965705LOCK_OBJECT_WRAPPER_INFO = { 
     
    967707      # A string for the target_func indicates a function by this name on the 
    968708      # instance rather is what should be wrapped. 
    969       {'target_func' : 'acquire', 
    970        'arg_checking_func' : allow_args_lock_acquire, 
    971        'return_checking_func' : allow_return_bool}, 
    972  
     709      {'func' : 'acquire', 
     710       'args' : [Bool()], 
     711       'return' : Bool()}, 
    973712  'release' : 
    974713      # A string for the target_func indicates a function by this name on the 
    975714      # instance rather is what should be wrapped. 
    976       {'target_func' : 'release', 
    977        'arg_checking_func' : allow_args_lock_release, 
    978        'return_checking_func' : allow_return_none}, 
     715      {'func' : 'release', 
     716       'args' : [], 
     717       'return' : None}, 
    979718} 
    980  
    981 def _require_virtual_namespace_object(virt): 
    982   if not isinstance(virt, virtual_namespace.VirtualNamespace): 
    983     raise NamespaceRequirementError 
    984  
    985 def allow_args_virtual_namespace_eval(virt, context): 
    986   _require_virtual_namespace_object(virt) 
    987   _require_dict_or_safedict(context) 
    988  
    989 def allow_return_safedict(context): 
    990   _require_safedict(context) 
    991  
    992719 
    993720VIRTUAL_NAMESPACE_OBJECT_WRAPPER_INFO = { 
     
    996723  # dict since that will screw up the references in the dict. 
    997724  'evaluate' : 
    998     { 
    999       'target_func' : 'evaluate', 
    1000       'arg_checking_func' : allow_args_virtual_namespace_eval, 
    1001       'return_checking_func' : allow_return_safedict 
    1002     } 
     725      {'func' : 'evaluate', 
     726       'args' : [DictOrSafeDict()], 
     727       'return' : SafeDict()}, 
    1003728} 
    1004729 
     
    1008733############################################################################## 
    1009734 
    1010 class NamespaceViolationError(Exception): 
    1011   """ 
    1012   This is the exception that will be raised to user code if there is any 
    1013   violation of the namespace rules, including illegal arguments and illegal 
    1014   return values. Note that user code can't catch this by name but they can 
    1015   catch it by catching all exceptions. 
    1016   """ 
     735def _copy(obj, objectmap=None): 
     736  """ 
     737  <Purpose> 
     738    Create a deep copy of an object without using the python 'copy' module. 
     739    Using copy.deepcopy() doesn't work because builtins like id and hasattr 
     740    aren't available when this is called. 
     741  <Arguments> 
     742    obj 
     743      The object to make a deep copy of. 
     744    objectmap 
     745      A mapping between original objects and the corresponding copy. This is 
     746      used to handle circular references. 
     747  <Exceptions> 
     748    TypeError 
     749      If an object is encountered that we don't know how to make a copy of. 
     750    NamespaceViolationError 
     751      If an unexpected error occurs while copying. This isn't the greatest 
     752      solution, but in general the idea is we just need to abort the wrapped 
     753      function call. 
     754  <Side Effects> 
     755    A new reference is created to every non-simple type of object. That is, 
     756    everything except objects of type str, unicode, int, etc. 
     757  <Returns> 
     758    The deep copy of obj with circular/recursive references preserved. 
     759  """ 
     760  try: 
     761    # If this is a top-level call to _copy, create a new objectmap for use 
     762    # by recursive calls to _copy. 
     763    if objectmap is None: 
     764      objectmap = {} 
     765    # If this is a circular reference, use the copy we already made. 
     766    elif _saved_id(obj) in objectmap: 
     767      return objectmap[_saved_id(obj)] 
     768 
     769    # types.InstanceType is included because the user can provide an instance 
     770    # of a class of their own in the list of callback args to settimer. 
     771    if _is_in(type(obj), [str, unicode, int, long, float, complex, bool, frozenset, 
     772                          types.NoneType, types.FunctionType, types.LambdaType, 
     773                          types.MethodType, types.InstanceType]): 
     774      return obj 
     775 
     776    elif type(obj) is list: 
     777      temp_list = [] 
     778      # Need to save this in the objectmap before recursing because lists 
     779      # might have circular references. 
     780      objectmap[_saved_id(obj)] = temp_list 
     781 
     782      for item in obj: 
     783        temp_list.append(_copy(item, objectmap)) 
     784 
     785      return temp_list 
     786 
     787    elif type(obj) is tuple: 
     788      temp_list = [] 
     789 
     790      for item in obj: 
     791        temp_list.append(_copy(item, objectmap)) 
     792 
     793      # I'm not 100% confident on my reasoning here, so feel free to point 
     794      # out where I'm wrong: There's no way for a tuple to directly contain 
     795      # a circular reference to itself. Instead, it has to contain, for 
     796      # example, a dict which has the same tuple as a value. In that 
     797      # situation, we can avoid infinite recursion and properly maintain 
     798      # circular references in our copies by checking the objectmap right 
     799      # after we do the copy of each item in the tuple. The existence of the 
     800      # dictionary would keep the recursion from being infinite because those 
     801      # are properly handled. That just leaves making sure we end up with 
     802      # only one copy of the tuple. We do that here by checking to see if we 
     803      # just made a copy as a result of copying the items above. If so, we 
     804      # return the one that's already been made. 
     805      if _saved_id(obj) in objectmap: 
     806        return objectmap[_saved_id(obj)] 
     807 
     808      retval = tuple(temp_list) 
     809      objectmap[_saved_id(obj)] = retval 
     810      return retval 
     811 
     812    elif type(obj) is set: 
     813      temp_list = [] 
     814      # We can't just store this list object in the objectmap because it isn't 
     815      # a set yet. If it's possible to have a set contain a reference to 
     816      # itself, this could result in infinite recursion. However, sets can 
     817      # only contain hashable items so I believe this can't happen. 
     818 
     819      for item in obj: 
     820        temp_list.append(_copy(item, objectmap)) 
     821 
     822      retval = set(temp_list) 
     823      objectmap[_saved_id(obj)] = retval 
     824      return retval 
     825 
     826    elif type(obj) is dict: 
     827      temp_dict = {} 
     828      # Need to save this in the objectmap before recursing because dicts 
     829      # might have circular references. 
     830      objectmap[_saved_id(obj)] = temp_dict 
     831 
     832      for key, value in obj.items(): 
     833        temp_key = _copy(key, objectmap) 
     834        temp_dict[temp_key] = _copy(value, objectmap) 
     835 
     836      return temp_dict 
     837 
     838    # We don't copy certain objects. This is because copying an emulated file 
     839    # object, for example, will cause the destructor of the original one to 
     840    # be invoked, which will close the actual underlying file. As the object 
     841    # is wrapped and the client does not have access to it, it's safe to not 
     842    # wrap it. 
     843    elif isinstance(obj, (NamespaceObjectWrapper, fakeapi.FileObj, 
     844                          fakeapi.TCPSocketObj, fakeapi.TCPServerSocketObj, 
     845                          fakeapi.UDPServerSocketObj,thread.LockType, 
     846                          virtual_namespace.VirtualNamespace)): 
     847      return obj 
     848 
     849    else: 
     850      raise TypeError("_copy is not implemented for objects of type " + str(type(obj))) 
     851 
     852  except Exception, e: 
     853    raise NamespaceInternalError("_copy failed on " + str(obj) + " with message " + str(e)) 
     854 
     855 
     856 
     857 
     858 
     859class NamespaceInternalError(Exception): 
     860  """Something went wrong and we should terminate.""" 
    1017861 
    1018862 
     
    1056900    self._wrapped__object = wrapped_object 
    1057901    self._wrapped__allowed_functions_dict = allowed_functions_dict 
    1058      
    1059  
    1060      
     902 
     903 
     904 
    1061905  def __getattr__(self, name): 
    1062906    """ 
     
    1068912    if name in self._wrapped__allowed_functions_dict: 
    1069913      wrapped_func = self._wrapped__allowed_functions_dict[name] 
    1070        
     914 
    1071915      def __do_func_call(*args, **kwargs): 
    1072916        return wrapped_func(self._wrapped__object, *args, **kwargs) 
    1073        
     917 
    1074918      return __do_func_call 
    1075      
     919 
    1076920    else: 
    1077921      # This is the standard way of handling "it doesn't exist as far as we 
     
    1103947    if "next" in self._wrapped__allowed_functions_dict: 
    1104948      return self._wrapped__allowed_functions_dict["next"](self._wrapped__object) 
    1105        
     949 
    1106950    raise TypeError("You tried to iterate a non-iterator of type " + str(type(self._wrapped__object))) 
    1107951 
     
    1146990  """ 
    1147991 
    1148   def _handle_violation(self, message): 
    1149     """ 
    1150     <Purpose> 
    1151       Perform necessary actions for when we detect some form of violation of 
    1152       the namespace. 
    1153     <Arguments> 
    1154       self 
    1155       message 
    1156         The message (a string) to be included with any raised exception or 
    1157         logged information. 
    1158     <Exception> 
    1159       NamespaceViolationError 
    1160         Raised always. Right now that's the purpose of this function. 
    1161     <Side Effects> 
    1162       None 
    1163     <Returns> 
    1164       None 
    1165     """ 
    1166     raise NamespaceViolationError("Namespace violation: " + message) 
    1167  
    1168  
    1169  
    1170   def _copy(self, obj, objectmap=None): 
    1171     """ 
    1172     <Purpose> 
    1173       Create a deep copy of an object without using the python 'copy' module. 
    1174       Using copy.deepcopy() doesn't work because builtins like id and hasattr 
    1175       aren't available when this is called. 
    1176     <Arguments> 
    1177       self 
    1178       obj 
    1179         The object to make a deep copy of. 
    1180       objectmap 
    1181         A mapping between original objects and the corresponding copy. This is 
    1182         used to handle circular references. 
    1183     <Exceptions> 
    1184       TypeError 
    1185         If an object is encountered that we don't know how to make a copy of. 
    1186       NamespaceViolationError 
    1187         If an unexpected error occurs while copying. This isn't the greatest 
    1188         solution, but in general the idea is we just need to abort the wrapped 
    1189         function call. 
    1190     <Side Effects> 
    1191       A new reference is created to every non-simple type of object. That is, 
    1192       everything except objects of type str, unicode, int, etc. 
    1193     <Returns> 
    1194       The deep copy of obj with circular/recursive references preserved. 
    1195     """ 
    1196     try: 
    1197       # If this is a top-level call to _copy, create a new objectmap for use 
    1198       # by recursive calls to _copy. 
    1199       if objectmap is None: 
    1200         objectmap = {} 
    1201       # If this is a circular reference, use the copy we already made. 
    1202       elif _saved_id(obj) in objectmap: 
    1203         return objectmap[_saved_id(obj)] 
    1204        
    1205       # types.InstanceType is included because the user can provide an instance 
    1206       # of a class of their own in the list of callback args to settimer. 
    1207       if _is_in(type(obj), [str, unicode, int, long, float, complex, bool, frozenset, 
    1208                             types.NoneType, types.FunctionType, types.LambdaType, 
    1209                             types.MethodType, types.InstanceType]): 
    1210         return obj 
    1211  
    1212       elif type(obj) is list: 
    1213         temp_list = [] 
    1214         # Need to save this in the objectmap before recursing because lists 
    1215         # might have circular references. 
    1216         objectmap[_saved_id(obj)] = temp_list 
    1217          
    1218         for item in obj: 
    1219           temp_list.append(self._copy(item, objectmap)) 
    1220            
    1221         return temp_list 
    1222  
    1223       elif type(obj) is tuple: 
    1224         temp_list = [] 
    1225  
    1226         for item in obj: 
    1227           temp_list.append(self._copy(item, objectmap)) 
    1228            
    1229         # I'm not 100% confident on my reasoning here, so feel free to point 
    1230         # out where I'm wrong: There's no way for a tuple to directly contain 
    1231         # a circular reference to itself. Instead, it has to contain, for 
    1232         # example, a dict which has the same tuple as a value. In that 
    1233         # situation, we can avoid infinite recursion and properly maintain 
    1234         # circular references in our copies by checking the objectmap right 
    1235         # after we do the copy of each item in the tuple. The existence of the 
    1236         # dictionary would keep the recursion from being infinite because those 
    1237         # are properly handled. That just leaves making sure we end up with 
    1238         # only one copy of the tuple. We do that here by checking to see if we 
    1239         # just made a copy as a result of copying the items above. If so, we 
    1240         # return the one that's already been made. 
    1241         if _saved_id(obj) in objectmap: 
    1242           return objectmap[_saved_id(obj)] 
    1243          
    1244         retval = tuple(temp_list) 
    1245         objectmap[_saved_id(obj)] = retval 
    1246         return retval 
    1247      
    1248       elif type(obj) is set: 
    1249         temp_list = [] 
    1250         # We can't just store this list object in the objectmap because it isn't 
    1251         # a set yet. If it's possible to have a set contain a reference to 
    1252         # itself, this could result in infinite recursion. However, sets can 
    1253         # only contain hashable items so I believe this can't happen. 
    1254  
    1255         for item in obj: 
    1256           temp_list.append(self._copy(item, objectmap)) 
    1257          
    1258         retval = set(temp_list) 
    1259         objectmap[_saved_id(obj)] = retval 
    1260         return retval 
    1261          
    1262       elif type(obj) is dict: 
    1263         temp_dict = {} 
    1264         # Need to save this in the objectmap before recursing because dicts 
    1265         # might have circular references. 
    1266         objectmap[_saved_id(obj)] = temp_dict 
    1267          
    1268         for key, value in obj.items(): 
    1269           temp_key = self._copy(key, objectmap) 
    1270           temp_dict[temp_key] = self._copy(value, objectmap) 
    1271            
    1272         return temp_dict 
    1273        
    1274       # We don't copy certain objects. This is because copying an emulated file 
    1275       # object, for example, will cause the destructor of the original one to 
    1276       # be invoked, which will close the actual underlying file. As the object 
    1277       # is wrapped and the client does not have access to it, it's safe to not 
    1278       # wrap it. 
    1279       elif isinstance(obj, (NamespaceObjectWrapper, emulfile.emulated_file, 
    1280                             emulcomm.emulated_socket, thread.LockType, 
    1281                             virtual_namespace.VirtualNamespace)): 
    1282         return obj 
    1283        
    1284       else: 
    1285         raise TypeError("_copy is not implemented for objects of type " + str(type(obj))) 
    1286        
    1287     except Exception, e: 
    1288       self._handle_violation("_copy failed on " + str(obj) + " with message " + str(e)) 
    1289  
    1290  
    1291  
    1292   def _check_arguments(self, *args, **kwargs): 
    1293     """ 
    1294     <Purpose> 
    1295       Check the arguments against the arg_checking_func provided to the 
    1296       constructor. 
    1297     <Arguments> 
    1298       self 
    1299       *args 
    1300       **kwargs 
    1301         The arguments that will ultimately be passed to the wrapped function. 
    1302     <Exceptions> 
    1303       TypeError 
    1304         If the arguments aren't valid. That is, if they fail the arg checking. 
    1305     <Side Effects> 
    1306       None 
    1307     <Returns> 
    1308       None 
    1309     """ 
    1310  
    1311     try: 
    1312       self.__arg_checking_func(*args, **kwargs) 
    1313  
    1314     except NamespaceRequirementError, e: 
    1315       if type(self.__target_func) is str: 
    1316         name = self.__target_func 
    1317       else: 
    1318         name = self.__target_func.__name__ 
    1319       raise TypeError("Function '" + name + "' called with incorrect arguments. " +  
    1320                       str(e) + " Arguments were args:" + str(args) + ", kwargs:" + str(kwargs)) 
    1321     # We catch a TypeError as some of the argument checking functions don't 
    1322     # accept variable args, so python will raise a TypeError if the correct 
    1323     # number of args/kwargs hasn't been passed. We don't show the exception 
    1324     # string in this case as it will name the argument checking function, 
    1325     # which is bound to confuse the user. Of course, confusion will result 
    1326     # if we have a bug in our code that is raising a TypeError. 
    1327     except TypeError: 
    1328       if type(self.__target_func) is str: 
    1329         name = self.__target_func 
    1330       else: 
    1331         name = self.__target_func.__name__ 
    1332       raise TypeError("Function '" + name + "' called with incorrect arguments. " +  
    1333                       " Arguments were args:" + str(args) + ", kwargs:" + str(kwargs)) 
    1334  
    1335  
    1336  
    1337   def _check_return_value(self, retval): 
    1338     """ 
    1339     <Purpose> 
    1340       Check the return value against the return_checking_func provided to the 
    1341       constructor. 
    1342     <Arguments> 
    1343       self 
    1344       retval 
    1345         The return value that will ultimately be returned to the calling code 
    1346         if it is acceptable. 
    1347     <Exceptions> 
    1348       NamespaceViolationError 
    1349         If the return value isn't acceptable. 
    1350     <Side Effects> 
    1351       None 
    1352     <Returns> 
    1353       None 
    1354     """ 
    1355  
    1356     try: 
    1357       self.__return_checking_func(retval) 
    1358     except NamespaceRequirementError, e: 
    1359       if type(self.__target_func) is str: 
    1360         name = self.__target_func 
    1361       else: 
    1362         name = self.__target_func.__name__ 
    1363       self._handle_violation("Function '" + name + "' returned with unallowed return type " +  
    1364                              str(type(retval)) + " : " + str(e)) 
    1365  
    1366  
    1367  
    1368   def _check_raised_exception(self, raised_exception): 
    1369     """ 
    1370     <Purpose> 
    1371       Check a raised exceptin against the exception_checking_func provided to 
    1372       the constructor. 
    1373     <Arguments> 
    1374       self 
    1375       raised_exception 
    1376         The exception that will ultimately be raised to the calling code if it 
    1377         is acceptable. 
    1378     <Exceptions> 
    1379       NamespaceViolationError 
    1380         If the exception isn't allowed. 
    1381     <Side Effects> 
    1382       None 
    1383     <Returns> 
    1384       None 
    1385     """ 
    1386  
    1387     try: 
    1388       self.__exception_checking_func(raised_exception) 
    1389     except NamespaceRequirementError: 
    1390       # We include the exception message because it might be a huge pain to 
    1391       # debug an error in our code without this. 
    1392       # TODO: this will lose the traceback info of the original exception. 
    1393       self._handle_violation("Exception of type " + str(type(raised_exception)) +  
    1394                              "is not an allowed exception type. " +  
    1395                              "Exception message was: " + str(raised_exception)) 
    1396  
    1397  
    1398  
    1399   def __init__(self, func_dict): 
     992  def __init__(self, func_dict, is_method=False): 
    1400993    """ 
    1401994    <Purpose> 
     
    1406999        A dictionary whose with the following keys whose values are the 
    14071000        corresponding funcion: 
    1408           target_func (required) -- a function or a string of the name 
     1001          func (required) -- a function or a string of the name 
    14091002            of the method on the underlying object. 
    1410           arg_checking_func (required) 
    1411           return_checking_func (required) 
    1412           exception_checking_func (optional) 
    1413           arg_wrapping_func (optional) 
    1414           arg_unwrapping_func (optional) 
    1415           return_wrapping_func (optional) 
     1003          args (required) 
     1004          return (required) 
     1005      is_method -- if this is an object's method being wrapped 
     1006            rather than a regular function. 
    14161007    <Exceptions> 
    14171008      None 
     
    14231014 
    14241015    # Required in func_dict.     
    1425     self.__target_func = func_dict["target_func"] 
    1426     self.__arg_checking_func = func_dict["arg_checking_func"] 
    1427     self.__return_checking_func = func_dict["return_checking_func"] 
    1428  
    1429     # Optional in func_dict. 
    1430     self.__exception_checking_func = func_dict.get("exception_checking_func", allow_all) 
    1431     self.__arg_wrapping_func = func_dict.get("arg_wrapping_func", None) 
    1432     self.__arg_unwrapping_func = func_dict.get("arg_unwrapping_func", None) 
    1433     self.__return_wrapping_func = func_dict.get("return_wrapping_func", None) 
     1016    self.__func = func_dict["func"] 
     1017    self.__args = func_dict["args"] 
     1018    self.__return = func_dict["return"] 
     1019    self.__is_method = is_method 
    14341020 
    14351021    # Make sure that the __target_func really is a function or a string 
    14361022    # indicating a function by that name on the underlying object should 
    14371023    # be called. 
    1438     if not _saved_callable(self.__target_func) and type(self.__target_func) is not str: 
    1439       raise TypeError("The target_func was neither callable nor a string when " +  
    1440                       "constructing a namespace-wrapped function. The object " +  
    1441                       "used for target_func was: " + repr(self.__target_func)) 
     1024    if not _saved_callable(self.__func) and type(self.__func) is not str: 
     1025      raise TypeError("The func was neither callable nor a string when " + 
     1026                      "constructing a namespace-wrapped function. The object " + 
     1027                      "used for target_func was: " + repr(self.__func)) 
     1028     
     1029    if type(self.__func) is str: 
     1030      self.__func_name = self.__func 
     1031    else: 
     1032      self.__func_name = self.__func.__name__ 
     1033 
     1034 
     1035 
     1036  def _process_args(self, args): 
     1037    args_to_return = [] 
     1038 
     1039    for index in range(len(args)): 
     1040      # We only copy simple types, which means we only copy ValueProcessor not 
     1041      # ObjectProcessor arguments. 
     1042      if isinstance(self.__args[index], ValueProcessor): 
     1043        temparg = self.__args[index].copy(args[index]) 
     1044      elif isinstance(self.__args[index], ObjectProcessor): 
     1045        print args[index] 
     1046        temparg = self.__args[index].unwrap(args[index]) 
     1047      else: 
     1048        raise NamespaceInternalError("Unknown retval expectation.") 
     1049 
     1050      self.__args[index].check(temparg) 
     1051 
     1052      args_to_return.append(temparg) 
     1053 
     1054    return args_to_return 
     1055 
     1056 
     1057 
     1058  def _process_retval_helper(self, processor, retval): 
     1059    if isinstance(processor, ValueProcessor): 
     1060      tempretval = processor.copy(retval) 
     1061      processor.check(tempretval) 
     1062    elif isinstance(processor, ObjectProcessor): 
     1063      processor.check(retval) 
     1064      tempretval = processor.wrap(retval) 
     1065    elif processor is None: 
     1066      if retval is not None: 
     1067        raise RepyArgumentError("Expected None but wasn't.") 
     1068      tempretval = None 
     1069    else: 
     1070      raise NamespaceInternalError("Unknown retval expectation.") 
     1071    return tempretval 
     1072 
     1073 
     1074 
     1075  def _process_retval(self, retval): 
     1076    try: 
     1077      # Allow the return value to be a tuple of processors. 
     1078      if type(retval) is tuple: 
     1079        if len(retval) != len(self.__return): 
     1080          raise RepyArgumentError("Returned tuple of wrong size.") 
     1081        tempretval = [] 
     1082        for index in range(len(retval)): 
     1083          tempitem = self._process_retval_helper(self.__return[index], retval[index]) 
     1084          tempretval.append(tempitem) 
     1085        tempretval = tuple(tempretval) 
     1086      else: 
     1087        tempretval = self._process_retval_helper(self.__return, retval) 
     1088 
     1089    except RepyArgumentError: 
     1090      raise 
     1091     
     1092    except Exception, e: 
     1093      raise NamespaceInternalError( 
     1094          "Function '" + self.__func_name + "' returned with unallowed return type " + 
     1095          str(type(retval)) + " : " + str(e)) 
     1096 
     1097    return tempretval 
    14421098 
    14431099 
     
    14641120    """ 
    14651121 
    1466     # Copy first, then check. 
    1467     args = self._copy(args) 
    1468     kwargs = self._copy(kwargs) 
    1469     self._check_arguments(*args, **kwargs) 
    1470  
    1471     if self.__arg_wrapping_func is not None: 
    1472       args, kwargs = self.__arg_wrapping_func(*args, **kwargs) 
    1473  
    1474     if self.__arg_unwrapping_func is not None: 
    1475       args, kwargs = self.__arg_unwrapping_func(*args, **kwargs) 
     1122    print args 
    14761123 
    14771124    try: 
     1125      # We don't allow keyword args. 
     1126      if kwargs: 
     1127        raise RepyArgumentError("Keyword arguments not allowed when calling %s." % 
     1128                                self.__func_name) 
     1129 
     1130      if self.__is_method: 
     1131        # This is a method of an object instance rather than a standalone function. 
     1132        # The "self" argument will be passed implicitly by python in some cases, so 
     1133        # we remove it from the args we check. For the others, we'll add it back in 
     1134        # after the check. 
     1135        args_to_check = args[1:] 
     1136      else: 
     1137        args_to_check = args 
     1138 
     1139      if len(args_to_check) != len(self.__args): 
     1140        if not self.__args or not isinstance(self.__args[-1:][0], NonCopiedVarArgs): 
     1141          raise RepyArgumentError("Wrong number of arguments (%s) when calling %s." % 
     1142                                  (len(args_to_check), self.__func_name)) 
     1143       
     1144      args_copy = self._process_args(args_to_check) 
     1145 
     1146      args_to_use = None 
     1147 
    14781148      # If it's a string rather than a function, then this is our convention 
    14791149      # for indicating that we want to wrap the function of this particular 
    14801150      # object. We use this if the function to wrap isn't available without 
    14811151      # having the object around, such as with real lock objects. 
    1482       if type(self.__target_func) is str: 
    1483         func_to_call = _saved_getattr(args[0], self.__target_func) 
    1484         # The "self" argument will be passed implicitly by python, so we remove 
    1485         # it from the args we pass to the function. 
    1486         args_without_self = args[1:] 
    1487         retval = func_to_call(*args_without_self, **kwargs) 
     1152      if type(self.__func) is str: 
     1153        func_to_call = _saved_getattr(args[0], self.__func) 
     1154        args_to_use = args_copy 
    14881155      else: 
    1489         retval = self.__target_func(*args, **kwargs) 
    1490        
    1491     except Exception, e: 
    1492       self._check_raised_exception(e) 
    1493        
    1494       # Armon: Do a normal "raise" rather than "raise e" so that the traceback 
    1495       # information is preserved. If we are in nested VirtualNamespace's we don't 
    1496       # want to reduce the traceback to only the lowest module on the stack. 
     1156        func_to_call = self.__func 
     1157        if self.__is_method: 
     1158          # Sanity check the object we're adding back in as the "self" argument. 
     1159          if not isinstance(args[0], (NamespaceObjectWrapper, fakeapi.FileObj, 
     1160                                      fakeapi.TCPSocketObj, fakeapi.TCPServerSocketObj, 
     1161                                      fakeapi.UDPServerSocketObj,thread.LockType, 
     1162                                      virtual_namespace.VirtualNamespace)): 
     1163            raise NamespaceInternalError("Wrong type for 'self' argument.") 
     1164          # If it's a method but the function was not provided as a string, we 
     1165          # actually do have to add the first argument back in. Yes, this whole 
     1166          # area of code is ugly. 
     1167          args_to_use = [args[0]] + args_copy 
     1168        else: 
     1169          args_to_use = args_copy 
     1170 
     1171      retval = func_to_call(*args_to_use) 
     1172         
     1173      return self._process_retval(retval) 
     1174 
     1175    except RepyError, e: 
     1176      # We allow any RepyError to continue up to the client code. 
    14971177      raise 
    14981178 
    1499     # Copy first, then check. 
    1500     retval = self._copy(retval) 
    1501     self._check_return_value(retval) 
    1502      
    1503     if self.__return_wrapping_func is not None: 
    1504       retval = self.__return_wrapping_func(retval) 
    1505  
    1506     return retval 
     1179    except: 
     1180      # Any other exception is unexpected, possibly a programming error on our 
     1181      # side, so we terminate. 
     1182      # TODO: log and terminate. 
     1183      import traceback 
     1184      traceback.print_exc() 
     1185      fakeapi.exitall()