+ def __str__(self):
+ return 'rsa-public("%s","%s")'%(self.e,self.n)
+
+# Possible properties of configuration nodes
+keywords={
+ 'contact':(email,"Contact address"),
+ 'dh':(dhgroup,"Diffie-Hellman group"),
+ 'hash':(hash,"Hash function"),
+ 'key-lifetime':(num,"Maximum key lifetime (ms)"),
+ 'setup-timeout':(num,"Key setup timeout (ms)"),
+ 'setup-retries':(num,"Maximum key setup packet retries"),
+ 'wait-time':(num,"Time to wait after unsuccessful key setup (ms)"),
+ 'renegotiate-time':(num,"Time after key setup to begin renegotiation (ms)"),
+ 'restrict-nets':(networks,"Allowable networks"),
+ 'networks':(networks,"Claimed networks"),
+ 'pubkey':(rsakey,"RSA public site key"),
+ 'peer':(single_ipaddr,"Tunnel peer IP address"),
+ 'address':(address,"External contact address and port"),
+ 'mobile':(boolean,"Site is mobile"),
+}
+
+def sp(name,value):
+ "Simply output a property - the default case"
+ return "%s %s;\n"%(name,value)
+
+# All levels support these properties
+global_properties={
+ 'contact':(lambda name,value:"# Contact email address: %s\n"%(value)),
+ 'dh':sp,
+ 'hash':sp,
+ 'key-lifetime':sp,
+ 'setup-timeout':sp,
+ 'setup-retries':sp,
+ 'wait-time':sp,
+ 'renegotiate-time':sp,
+ 'restrict-nets':(lambda name,value:"# restrict-nets %s\n"%value),
+}
+
+class level:
+ "A level in the configuration hierarchy"
+ depth=0
+ leaf=0
+ allow_properties={}
+ require_properties={}
+ def __init__(self,w):
+ self.name=w[1]
+ self.properties={}
+ self.children={}
+ def indent(self,w,t):
+ w.write(" "[:t])
+ def prop_out(self,n):
+ return self.allow_properties[n](n,str(self.properties[n]))
+ def output_props(self,w,ind):
+ for i in self.properties.keys():
+ if self.allow_properties[i]:
+ self.indent(w,ind)
+ w.write("%s"%self.prop_out(i))
+ def output_data(self,w,ind,np):
+ self.indent(w,ind)
+ w.write("%s {\n"%(self.name))
+ self.output_props(w,ind+2)
+ if self.depth==1: w.write("\n");
+ for c in self.children.values():
+ c.output_data(w,ind+2,np+self.name+"/")
+ self.indent(w,ind)
+ w.write("};\n")
+
+class vpnlevel(level):
+ "VPN level in the configuration hierarchy"
+ depth=1
+ leaf=0
+ type="vpn"
+ allow_properties=global_properties.copy()
+ require_properties={
+ 'contact':"VPN admin contact address"
+ }
+ def __init__(self,w):
+ level.__init__(self,w)
+ def output_vpnflat(self,w,ind,h):
+ "Output flattened list of site names for this VPN"
+ self.indent(w,ind)
+ w.write("%s {\n"%(self.name))
+ for i in self.children.keys():
+ self.children[i].output_vpnflat(w,ind+2,
+ h+"/"+self.name+"/"+i)
+ w.write("\n")
+ self.indent(w,ind+2)
+ w.write("all-sites %s;\n"%
+ string.join(self.children.keys(),','))
+ self.indent(w,ind)
+ w.write("};\n")
+
+class locationlevel(level):
+ "Location level in the configuration hierarchy"
+ depth=2
+ leaf=0
+ type="location"
+ allow_properties=global_properties.copy()
+ require_properties={
+ 'contact':"Location admin contact address",
+ }
+ def __init__(self,w):
+ level.__init__(self,w)
+ self.group=w[2]
+ def output_vpnflat(self,w,ind,h):
+ self.indent(w,ind)
+ # The "h=h,self=self" abomination below exists because
+ # Python didn't support nested_scopes until version 2.1
+ w.write("%s %s;\n"%(self.name,string.join(
+ map(lambda x,h=h,self=self:
+ h+"/"+x,self.children.keys()),',')))
+
+class sitelevel(level):
+ "Site level (i.e. a leafnode) in the configuration hierarchy"
+ depth=3
+ leaf=1
+ type="site"
+ allow_properties=global_properties.copy()
+ allow_properties.update({
+ 'address':sp,
+ 'networks':None,
+ 'peer':None,
+ 'pubkey':(lambda n,v:"key %s;\n"%v),
+ 'address':(lambda n,v:"address %s;\n"%v),
+ 'mobile':sp,
+ })
+ require_properties={
+ 'dh':"Diffie-Hellman group",
+ 'contact':"Site admin contact address",
+ 'networks':"Networks claimed by the site",
+ 'hash':"hash function",
+ 'peer':"Gateway address of the site",
+ 'pubkey':"RSA public key of the site",
+ }