856 |
857 |
858 | """
859 | return retstr
860 |
861 | def check_wg_key(wgkey):
862 | wg_keylen = 32
863 | if len(wgkey) > wg_keylen*2:
864 | raise ValueError(f"Wireguard key {wgkey} too long")
865 | base64_valid_chars = set(string.ascii_letters + string.digits + "+/=")
866 | if not set(wgkey).issubset(base64_valid_chars):
867 | raise ValueError(f"Wireguard key {wgkey} contains invalid character: {set(filter(lambda x:x not in base64_valid_chars,wgkey))}")
868 | key_raw = base64.b64decode(wgkey)
869 | if len(key_raw) != 32:
870 | raise ValueError(f"Wireguard key {wgkey} are not {wg_keylen} bytes len")
871 |
872 | def check_valid_ip_range(af,IPranges,ip,name,only_ip = True):
873 | if af == "IPv4":
874 | IPNet = IPv4Network
875 | IPInt = IPv4Interface
876 | elif af == "IPv6":
877 | IPNet = IPv6Network
878 | IPInt = IPv6Interface
879 | else:
880 | raise ValueError("Unknown af:",af)
881 | if ip == None or ip == "":
882 | raise ValueError(f"{str(ip)} is not a vaild IPv4 or IPv6 address")
883 | if only_ip:
884 | if "/" in ip:
885 | raise ValueError(ip + " is not a valid IPv4 or IPv6 address, you may need to remove /" + ip.split("/")[1])
886 | if IPNet(ip).num_addresses != 1:
887 | raise ValueError(ip + " contains more than one IP")
888 | for iprange in IPranges:
889 | if IPNet(iprange,strict=False).supernet_of(IPInt(ip).network):
890 | return True
891 | raise ValueError(ip + " are not in " + name + " range: " + str(IPranges))
892 |
893 | async def check_asn_ip(admin,mntner,asn,af,ip,only_ip=True):
894 | if af == "IPv4":
895 | IPNet = IPv4Network
896 | IPInt = IPv4Interface
897 | allowed = DN42_valid_ipv4s
898 | descr = "DN42 IPv4"
899 | elif af == "IPv6":
900 | IPNet = IPv6Network
901 | IPInt = IPv6Interface
902 | allowed = DN42_valid_ipv6s
903 | descr = "DN42 IPv6"
904 | else:
905 | raise ValueError("Unknown af:",af)
906 | check_valid_ip_range(af,IPranges=allowed,ip=ip,name=descr,only_ip=only_ip)
907 | peerIP_info = DN42whois.proc_data((await whois_query(ip)))
908 | originASN = ["nobody"]
909 | ipowner = []
910 | if "origin" in peerIP_info:
911 | originASN = peerIP_info["origin"]
912 | if asn in peerIP_info["origin"]:
913 | return True
914 | if "mnt-by" in peerIP_info:
915 | if "DN42-MNT" in peerIP_info["mnt-by"]:
916 | peerIP_info["mnt-by"].remove("DN42-MNT")
917 | ipowner += peerIP_info["mnt-by"]
918 | if bool(set(mntner) & set(peerIP_info["mnt-by"])):
919 | return True
920 | if "admin-c" in peerIP_info:
921 | ipowner += peerIP_info["admin-c"]
922 | if bool(set(admin) & set(peerIP_info["admin-c"])):
923 | return True
924 | raise PermissionError("IP " + ip + f" owned by {originASN}({set(ipowner)}) instead of {asn}({set(admin + mntner)})")
925 |
926 | async def check_reg_paramater(paramaters,skip_check=None,git_pull=True,allow_invalid_as=False,allowed_custom_myip=[]):
927 | if (paramaters["hasIPV4"] or paramaters["hasIPV4LL"] or paramaters["hasIPV6"] or paramaters["hasIPV6LL"]) == False:
928 | raise ValueError("You can't peer without any IP.")
929 | if paramaters["peerASN"] == "AS" + paramaters["myASN"]:
930 | raise ValueError("You can't peer with my ASN.")
931 | try:
932 | mntner,admin = await get_info_from_asn(paramaters["peerASN"])
933 | except FileNotFoundError as e:
934 | if allow_invalid_as:
935 | mntner,admin = [["DN42-MNT"],["BURBLE-DN42"]]
936 | else:
937 | raise e
938 | ######################### hasIPV4
939 | if paramaters["hasIPV4"]:
940 | if paramaters["myIPV4"] == None:
941 | raise NotImplementedError("Sorry, I don't have IPv4 address.")
942 | await check_asn_ip(admin,mntner,paramaters['peerASN'],"IPv4",paramaters["peerIPV4"],only_ip=True)
943 | if paramaters["myIPV4"] == my_paramaters["myIPV4"] or paramaters["myIPV4"] in allowed_custom_myip:
944 | pass
945 | else:
946 | if "/" not in paramaters["myIPV4"]:
947 | await check_asn_ip(admin,mntner,paramaters['peerASN'],"IPv4",paramaters["myIPV4"],only_ip=True)
948 | else:
949 | await check_asn_ip(admin,mntner,paramaters['peerASN'],"IPv4",paramaters["myIPV4"],only_ip=False)
950 | check_valid_ip_range("IPv4",[paramaters["myIPV4"]],paramaters["peerIPV4"],"allocated IPv4 to me")
951 | else:
952 | paramaters["peerIPV4"] = None
953 | ######################### hasIPV6
954 | if paramaters["hasIPV6"]:
955 | if paramaters["myIPV6"] == None:
956 | raise NotImplementedError("Sorry, I don't have IPv6 address.")
957 | await check_asn_ip(admin,mntner,paramaters['peerASN'],"IPv6",paramaters["peerIPV6"],only_ip=True)
958 | if paramaters["myIPV6"] == my_paramaters["myIPV6"] or paramaters["myIPV6"] in allowed_custom_myip:
959 | pass
960 | else:
961 | if "/" not in paramaters["myIPV6"]:
962 | await check_asn_ip(admin,mntner,paramaters['peerASN'],"IPv6",paramaters["myIPV6"],only_ip=True)
963 | else:
964 | await check_asn_ip(admin,mntner,paramaters['peerASN'],"IPv6",paramaters["myIPV6"],only_ip=False)
965 | check_valid_ip_range("IPv6",[paramaters["myIPV6"]],paramaters["peerIPV6"],"allocated IPv6 to me")
966 | else:
967 | paramaters["peerIPV6"] = None
968 | ######################### hasIPV4LL
969 | if paramaters["hasIPV4LL"]:
970 | if paramaters["myIPV4LL"] == None:
971 | raise NotImplementedError("Sorry, I don't have IPv4 link-local address.")
972 | check_valid_ip_range("IPv4",[valid_ipv4_lilo],paramaters["peerIPV4LL"],"link-local ipv4")
973 | check_valid_ip_range("IPv4",[valid_ipv4_lilo],paramaters["myIPV4LL"].split("/")[0],"link-local ipv4")
974 | paramaters["myIPV4LL"] = paramaters["myIPV4LL"].split("/")[0] + "/" + valid_ipv4_lilo.split("/")[1]
975 | else:
976 | paramaters["peerIPV4LL"] = None
977 | ######################### hasIPV6LL
978 | if paramaters["hasIPV6LL"]:
979 | if paramaters["myIPV6LL"] == None:
980 | raise NotImplementedError("Sorry, I don't have IPv6 link-local address.")
981 | check_valid_ip_range("IPv6",[valid_ipv6_lilo],paramaters["peerIPV6LL"],"link-local ipv6")
982 | check_valid_ip_range("IPv6",[valid_ipv6_lilo],paramaters["myIPV6LL"].split("/")[0],"link-local ipv6")
983 | paramaters["myIPV6LL"] = paramaters["myIPV6LL"].split("/")[0] + "/" + valid_ipv6_lilo.split("/")[1]
984 | else:
985 | paramaters["peerIPV6LL"] = None
986 | if paramaters["MP_BGP"]:
987 | if not (paramaters["hasIPV6"] or paramaters["hasIPV6LL"]):
988 | raise ValueError("Value Error. You need a IPv6 address to use multiprotocol BGP.")
989 | if not paramaters["Ext_Nh"]:
990 | if not (paramaters["hasIPV4"] or paramaters["hasIPV4LL"]):
991 | raise ValueError("Value Error. You need a IPv4 address to enable multiprotocol BGP unless you support extended next hop.")
992 | if paramaters["Ext_Nh"]:
993 | if not (paramaters["hasIPV6"] or paramaters["hasIPV6LL"]):
994 | raise ValueError("Value Error. You need a IPv6 address to use extended next hop.")
995 | if not paramaters["MP_BGP"]:
996 | raise ValueError("Value Error. You need enable multiprotocol BGP to use extended next hop.")
997 | if paramaters["allowExtNh"] == False:
998 | raise NotImplementedError("Sorry, I don't support extended next hop.")
999 |
1000 | if paramaters["peerWG_PS_Key"] == "":
1001 | paramaters["peerWG_PS_Key"] == None
1002 | if paramaters["customDevice"] == None:
1003 | if paramaters["hasHost"]:
1004 | if paramaters["peerHost"] == None and (my_paramaters["myHost"] == None):
1005 | raise ValueError("Sorry, I don't have a public IP so that your endpoint can't be null.")
1006 | if paramaters["peerHost"] == None or ":" not in paramaters["peerHost"]:
1007 | raise ValueError("Parse Error, Host must looks like address:port.")
1008 | hostaddr,port = paramaters["peerHost"].rsplit(":",1)
1009 | port = int(port)
1010 | if hostaddr[0] == "[" and hostaddr[-1] == "]":
1011 | hostaddr = hostaddr[1:-1]
1012 | elif ":" in hostaddr:
1013 | raise ValueError(f"Parse Error, IPv6 Address as endpoint, it should be like [{hostaddr}]:{port}.")
1014 | addrinfo = socket.getaddrinfo(hostaddr,port)
1015 | else:
1016 | paramaters["peerHost"] = None
1017 | peerKey = paramaters["peerWG_Pub_Key"]
1018 | if peerKey == None or len(peerKey) == 0:
1019 | raise ValueError('"Your WG Public Key" can\'t be null.')
1020 | if peerKey == paramaters["myWG_Pub_Key"]:
1021 | raise ValueError('You can\'t use my wireguard public key as your wireguard public key.')
1022 | check_wg_key(peerKey)
1023 | check_wg_key(paramaters["myWG_Pri_Key"])
1024 | else:
1025 | paramaters["peerWG_Pub_Key"] = ""
1026 | paramaters["myWG_Pub_Key"] = ""
1027 | if git_pull:
1028 | RRstate_repo.pull()
1029 | conf_dir = wgconfpath + "/peerinfo"
1030 | if os.path.isdir(conf_dir): #Check this node hasn't peer with us before
1031 | for old_conf_file in os.listdir(conf_dir):
1032 | if old_conf_file.endswith(".yaml") and os.path.isfile(f"{conf_dir}/{old_conf_file}"):
1033 | if skip_check != None and old_conf_file[:-5] == str(skip_check):
1034 | continue
1035 | old_conf = yaml.load(open(f"{conf_dir}/{old_conf_file}").read(),Loader=yaml.SafeLoader)
1036 | if paramaters["peerIPV4"] != None and old_conf["peerIPV4"] == paramaters["peerIPV4"]:
1037 | raise FileExistsError(f'This IPv4 address {paramaters["peerIPV4"]} already exisis in "{old_conf_file}", please remove the peering first.')
1038 | if paramaters["peerIPV6"] != None and old_conf["peerIPV6"] == paramaters["peerIPV6"]:
1039 | raise FileExistsError(f'This IPv6 address {paramaters["peerIPV6"]} already exisis in "{old_conf_file}", please remove the peering first.')
1040 | if old_conf["peerHost"] != None and old_conf["peerHost"] == paramaters["peerHost"]:
1041 | raise FileExistsError(f'This endpoint "{paramaters["peerHost"]}" already exisis in "{old_conf_file}", please remove the peering first.')
1042 | if old_conf["peerASN"] != None and old_conf["peerASN"] == paramaters["peerASN"]:
1043 | raise FileExistsError(f'This ASN "{paramaters["peerASN"]}" already exisis in "{old_conf_file}", please remove the peering or update it first.')
1044 | return paramaters
1045 |
1046 | def replace_str(text,replace):
1047 | for k,v in replace.items():
1048 | text = text.replace(k,v)
1049 | return text
1050 |
1051 | def indent2(text,fill):
1052 | if "\n" not in text:
1053 | return text
1054 | tail,body = text.split("\n",1)
1055 | return tail + "\n" + textwrap.indent(body,fill)
1056 |
1057 | def get_peeronly_filter(io,af,peerASN,myASN,noExport=True):
1058 | commadd = ""
1059 | if noExport == True:
1060 | commadd = "bgp_community.add((65535,65281));"
1061 | if io == "i":
1062 | if af == 4:
1063 | return textwrap.dedent(f"""\
1064 | if is_valid_network() && bgp_path.last = {peerASN} then {{
1065 | if (roa_check(dn42_roa, net, bgp_path.last) != ROA_VALID) then {{
1066 | print "[dn42] ROA check failed from ",bgp_path.first , " ifname:", ifname ," for ", net, " ASN:", bgp_path.last;
1067 | reject;
1068 | }}
1069 | {commadd}
1070 | accept;
1071 | }}
1072 | reject;
1073 | """)
1074 | elif af == 6:
1075 | return textwrap.dedent(f"""\
1076 | if is_valid_network_v6() && bgp_path.last = {peerASN} then {{
1077 | if (roa_check(dn42_roa_v6, net, bgp_path.last) != ROA_VALID) then {{
1078 | print "[dn42] ROA check failed from ",bgp_path.first , " ifname:", ifname ," for ", net, " ASN:", bgp_path.last;
1079 | reject;
1080 | }}
1081 | {commadd}
1082 | accept;
1083 | }}
1084 | reject;
1085 | """)
1086 | if io == "o":
1087 | if af == 4:
1088 | return textwrap.dedent(f"""\
1089 | if (bgp_path.last = {myASN} && (roa_check(dn42_roa, net, bgp_path.last) = ROA_VALID)) || (is_self_net() && is_valid_network() && source ~ [RTS_STATIC, RTS_BGP]) then{{
1090 | if is_valid_network() then{{
1091 | {commadd}
1092 | accept;
1093 | }}
1094 | }}
1095 | reject;
1096 | """)
1097 | elif af == 6:
1098 | return textwrap.dedent(f"""\
1099 | if (bgp_path.last = {myASN} && (roa_check(dn42_roa_v6, net, bgp_path.last) = ROA_VALID)) || (is_self_net_v6() && is_valid_network_v6() && source ~ [RTS_STATIC, RTS_BGP]) then{{
1100 | if is_valid_network_v6() then{{
1101 | {commadd}
1102 | accept;
1103 | }}
1104 | }}
1105 | reject;
1106 | """)
1107 | def get_transit_filter(io,af,peerASN,myASN,noExport=True):
1108 | commadd = ""
1109 | if noExport == True:
1110 | commadd = "bgp_community.add((65535,65281));"
1111 | if io == "i":
1112 | if af == 4:
1113 | return textwrap.dedent(f"""\
1114 | {commadd}
1115 | if is_valid_network() && !is_self_net() then {{
1116 | if (roa_check(dn42_roa, net, bgp_path.last) != ROA_VALID) then {{
1117 | print "[dn42] ROA check failed from ",bgp_path.first , " ifname:", ifname ," for ", net, " ASN:", bgp_path.last;
1118 | reject;
1119 | }}
1120 | {commadd}
1121 | accept;
1122 | }}
1123 | reject;
1124 | """)
1125 | elif af == 6:
1126 | return textwrap.dedent(f"""\
1127 | {commadd}
1128 | if is_valid_network_v6() && !is_self_net_v6() then {{
1129 | if (roa_check(dn42_roa_v6, net, bgp_path.last) != ROA_VALID) then {{
1130 | print "[dn42] ROA check failed from ",bgp_path.first , " ifname:", ifname ," for ", net, " ASN ", bgp_path.last;
1131 | reject;
1132 | }}
1133 | {commadd}
1134 | accept;
1135 | }}
1136 | reject;
1137 | """)
1138 | if io == "o":
1139 | if af == 4:
1140 | return textwrap.dedent(f"""\
1141 | {commadd}
1142 | if is_valid_network() && source ~ [RTS_STATIC, RTS_BGP] then {{
1143 | accept;
1144 | }}
1145 | reject;
1146 | """)
1147 | elif af == 6:
1148 | return textwrap.dedent(f"""\
1149 | {commadd}
1150 | if is_valid_network_v6() && source ~ [RTS_STATIC, RTS_BGP] then {{
1151 | accept;
1152 | }}
1153 | reject;
1154 | """)
1155 | def get_ix_filter(io,af,peerASN,myASN,noExport=True):
1156 | if io == "i":
1157 | if af == 4:
1158 | return textwrap.dedent(f"""\
1159 | if (bgp_path.last = bgp_path.first && (roa_check(dn42_roa, net, bgp_path.last) = ROA_VALID)) then{{
1160 | if is_valid_network() then{{
1161 | accept;
1162 | }}
1163 | }}
1164 | reject;
1165 | """)
1166 | elif af == 6:
1167 | return textwrap.dedent(f"""\
1168 | if (bgp_path.last = bgp_path.first && (roa_check(dn42_roa_v6, net, bgp_path.last) = ROA_VALID)) then{{
1169 | if is_valid_network_v6() then{{
1170 | accept;
1171 | }}
1172 | }}
1173 | reject;
1174 | """)
1175 |
1176 | def newConfig(paramaters,overwrite=False):
1177 | peerASN = int(paramaters["peerASN"][2:])
1178 | peerKey = paramaters["peerWG_Pub_Key"]
1179 | peerPSK = paramaters["peerWG_PS_Key"]
1180 | peerContact = paramaters["peerContact"]
1181 | peerName = paramaters["peerName"]
1182 | peerID = paramaters["PeerID"]
1183 | peerHost = paramaters["peerHost"]
1184 | peerIPV4 = paramaters["peerIPV4"]
1185 | peerIPV6 = paramaters["peerIPV6"]
1186 | peerIPV4LL = paramaters["peerIPV4LL"]
1187 | peerIPV6LL = paramaters["peerIPV6LL"]
1188 | transitMode = paramaters["transitMode"]
1189 | MP_BGP = paramaters["MP_BGP"]
1190 | Ext_Nh = paramaters["Ext_Nh"]
1191 | myIPV4 = paramaters["myIPV4"]
1192 | myIPV6 = paramaters["myIPV6"]
1193 | myIPV4LL = paramaters["myIPV4LL"]
1194 | myIPV6LL = paramaters["myIPV6LL"]
1195 | myhost = paramaters["myHost"]
1196 | myasn = paramaters["myASN"][2:]
1197 | privkey = paramaters["myWG_Pri_Key"]
1198 | publkey = paramaters["myWG_Pub_Key"]
1199 | birdAddConf = paramaters["birdAddConf"]
1200 | mtu = paramaters["myWG_MTU"]
1201 | customDevice = paramaters["customDevice"]
1202 | customDeviceSetup = paramaters["customDeviceSetup"]
1203 | successdisplay = { "My ASN": paramaters["myASN"]}
1204 | peerV4use = None
1205 | myV4use = None
1206 | myV4useIP = None
1207 | if peerIPV4LL != None:
1208 | peerV4use = peerIPV4LL
1209 | myV4use = myIPV4LL
1210 | myV4useIP = myIPV4LL.split("/")[0] if myIPV4LL != None else None
1211 | successdisplay["IPv4 Link local"] = myV4useIP
1212 | if IPv4Address(peerV4use) == IPv4Address(myV4useIP):
1213 | raise ValueError("Your tunnel IPv6 link local address are conflicted with mine: " + str(IPv4Address(peerV4use)))
1214 | elif peerIPV4 != None:
1215 | peerV4use = peerIPV4
1216 | myV4use = myIPV4
1217 | myV4useIP = myIPV4.split("/")[0] if myIPV4 != None else None
1218 | successdisplay["DN42 IPv4"] = myV4useIP
1219 | if IPv4Address(peerV4use) == IPv4Address(myV4useIP):
1220 | raise ValueError("Your tunnel IPv4 address are conflicted with mine: " + str(IPv4Address(peerV4use)))
1221 | peerV6use =None
1222 | myV6use = None
1223 | myV6useIP =None
1224 | if peerIPV6LL != None:
1225 | peerV6use = peerIPV6LL
1226 | myV6use = myIPV6LL
1227 | myV6useIP = myIPV6LL.split("/")[0] if myIPV6LL != None else None
1228 | successdisplay["IPv6 Link local"] = myV6useIP
1229 | if IPv6Address(peerV6use) == IPv6Address(myV6useIP):
1230 | raise ValueError("Your tunnel IPv6 link local address are conflicted with mine: " + str(IPv6Address(peerV6use)))
1231 | elif peerIPV6 != None:
1232 | peerV6use = peerIPV6
1233 | myV6use = myIPV6
1234 | myV6useIP = myIPV6.split("/")[0] if myIPV6 != None else None
1235 | successdisplay["DN42 IPv6"] = myV6useIP
1236 | if IPv6Address(peerV6use) == IPv6Address(myV6useIP):
1237 | raise ValueError("Your tunnel IPv6 address are conflicted with mine: " + str(IPv6Address(peerV6use)))
1238 | if peerContact == None or len(peerContact) == 0:
1239 | raise ValueError('"Your Telegram ID or e-mail" can\'t be null.')
1240 | portlist = list(sorted(map(lambda x:int(x.split(".")[0]),filter(lambda x:x[-4:] == "yaml", os.listdir(wgconfpath + "/peerinfo")))))
1241 | if peerID == None:
1242 | port_range = eval(my_config["wg_port_search_range"])
1243 | for p in port_range:
1244 | try:
1245 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
1246 | sock.bind(("0.0.0.0", p))
1247 | sock.close()
1248 | except Exception as e:
1249 | try:
1250 | sock.close()
1251 | except Exception as e:
1252 | pass
1253 | print(f"Try peer ID:{p} failed, reason:{e}")
1254 | continue
1255 | if p not in portlist:
1256 | peerID = p
1257 | print("Select peer ID:", peerID)
1258 | break
1259 | print(f"Try peer ID:{p} failed, reason: exists in portlist")
1260 | else:
1261 | peerID = int(peerID)
1262 | if peerID == None:
1263 | raise IndexError("PeerID not available, contact my to peer manually. ")
1264 | if peerID in portlist and overwrite == False:
1265 | raise IndexError("PeerID already exists.")
1266 | paramaters["PeerID"] = peerID
1267 | #if peerName == None:
1268 | peerContact_sess = peerContact
1269 | if peerContact_sess.startswith("https://t.me/"):
1270 | peerContact_sess = peerContact_sess.split("https://t.me/",1)[1]
1271 | if peerContact_sess.startswith("https://"):
1272 | peerContact_sess = peerContact_sess.split("https://",1)[1]
1273 | if "@" in peerContact_sess:
1274 | username, domain = peerContact_sess.split("@",1)
1275 | if username in ["dn42","admin","root","abuse","user","tg","telegram","irc","skype","git","whatsapp"]:
1276 | peerContact_sess = domain
1277 | elif username != "":
1278 | peerContact_sess = username + "_" + domain
1279 | peerName = str(int(peerID) % 10000).zfill(4) + peerContact_sess
1280 | peerName = peerName.replace("-","_")
1281 | peerName = re.sub(r"[^A-Za-z0-9_]+", '', peerName)
1282 | peerName = peerName[:10]
1283 |
1284 | if customDevice == None:
1285 | if_name = "dn42-" + peerName
1286 | else:
1287 | if_name = customDevice
1288 | customDeviceSetup = customDeviceSetup.replace( "%if_name" , if_name )
1289 | if peerHost != None:
1290 | customDeviceSetup = customDeviceSetup.replace( "%peer_host" , peerHost )
1291 |
1292 |
1293 | wgconf = textwrap.dedent(f"""\
1294 | [Interface]
1295 | PrivateKey = { privkey }
1296 | ListenPort = { str(peerID) }
1297 | [Peer]
1298 | PublicKey = { peerKey }
1299 | AllowedIPs = { ", ".join( wg_allowed_ips ) }
1300 | """)
1301 | if peerHost != None and peerHost != "":
1302 | wgconf += f"Endpoint = { peerHost }\n"
1303 | if peerPSK != None and peerPSK != "":
1304 | wgconf += f"PresharedKey = { peerPSK }\n"
1305 |
1306 | wgsh = textwrap.dedent(f"""\
1307 | ip link add dev {if_name} type wireguard
1308 | wg setconf {if_name} {wgconfpath}/{peerName}.conf
1309 | """)
1310 | setupsh = textwrap.dedent(f"""\
1311 | ip link set {if_name} up
1312 | """)
1313 | if int(mtu) > 0:
1314 | setupsh += textwrap.dedent(f"""\
1315 | ip link set mtu {mtu} dev {if_name}
1316 | """)
1317 | if use_speed_limit:
1318 | setupsh += f"wondershaper {if_name} $WG_SPEED_LIMIT $WG_SPEED_LIMIT || true\n"
1319 |
1320 | birdPeerV4 = peerV4use
1321 | birdMyV4 = myV4useIP
1322 | birdPeerV6 = peerV6use
1323 | birdMyV6 = myV6useIP
1324 | if peerV4use != None:
1325 | if peerV6use != None and MP_BGP == True and Ext_Nh == True:
1326 | birdPeerV4 = None
1327 | birdMyV4 = None
1328 | if Ext_Nh == False:
1329 | if "/" in myV4use:
1330 | setupsh += f"ip addr add {myV4use} dev {if_name} scope link\n"
1331 | else:
1332 | setupsh += f"ip addr add {myV4useIP} peer {peerIPV4} dev {if_name} scope link\n"
1333 |
1334 | if peerV6use != None:
1335 | if "/" in myV6use:
1336 | setupsh += f"ip addr add {myV6use} dev {if_name} scope link\n"
1337 | else:
1338 | setupsh += f"ip addr add {myV6useIP} peer {peerV6use} dev {if_name}\n"
1339 | setupsh += f"ip route add {peerV6use}/128 src {myV6useIP} dev {if_name}\n"
1340 |
1341 | birdconf = ""
1342 | channel4 = ""
1343 | channel6 = ""
1344 | filter4i = ""
1345 | filter4e = ""
1346 | filter6i = ""
1347 | filter6e = ""
1348 |
1349 | if Ext_Nh == True:
1350 | channel4 += "extended next hop on;\n"
1351 | if transitMode == "Regular" or transitMode == "Private Peering":
1352 | pass
1353 | elif transitMode == "PeerOnly" or transitMode == "Public Peering":
1354 | filter4i += get_peeronly_filter("i",4,peerASN,myasn,True)
1355 | filter6i += get_peeronly_filter("i",6,peerASN,myasn,True)
1356 | filter4e += get_peeronly_filter("o",4,peerASN,myasn,True)
1357 | filter6e += get_peeronly_filter("o",6,peerASN,myasn,True)
1358 | elif transitMode == "Upstream" or transitMode == "Transit Providers":
1359 | filter4i += get_transit_filter("i",4,peerASN,myasn,True)
1360 | filter6i += get_transit_filter("i",6,peerASN,myasn,True)
1361 | filter4e += get_peeronly_filter("o",4,peerASN,myasn,False)
1362 | filter6e += get_peeronly_filter("o",6,peerASN,myasn,False)
1363 | elif transitMode == "Downstream" or transitMode == "Customer":
1364 | filter4i += get_peeronly_filter("i",4,peerASN,myasn,False)
1365 | filter6i += get_peeronly_filter("i",6,peerASN,myasn,False)
1366 | filter4e += get_transit_filter("o",4,peerASN,myasn,True)
1367 | filter6e += get_transit_filter("o",6,peerASN,myasn,True)
1368 | elif transitMode == "IX":
1369 | filter4i += get_ix_filter("i",4,peerASN,myasn,False)
1370 | filter6i += get_ix_filter("i",6,peerASN,myasn,False)
1371 | filter4e += get_peeronly_filter("o",4,peerASN,myasn,False)
1372 | filter6e += get_peeronly_filter("o",6,peerASN,myasn,False)
1373 | else:
1374 | raise ValueError("Unknow transitMode: " + transitMode)
1375 | if "chan4" in birdAddConf:
1376 | channel4 += "\n".join(birdAddConf["chan4"]) + "\n"
1377 | if "chan6" in birdAddConf:
1378 | channel6 += "\n".join(birdAddConf["chan6"]) + "\n"
1379 | if "filter4i" in birdAddConf:
1380 | filter4i += "\n".join(birdAddConf["filter4i"]) + "\n"
1381 | if "filter6i" in birdAddConf:
1382 | filter4e += "\n".join(birdAddConf["filter6i"]) + "\n"
1383 | if "filter4e" in birdAddConf:
1384 | filter6i += "\n".join(birdAddConf["filter4e"]) + "\n"
1385 | if "filter6e" in birdAddConf:
1386 | filter6e += "\n".join(birdAddConf["filter6e"]) + "\n"
1387 | #########################
1388 | if filter4i != "":
1389 | channel4 += textwrap.dedent(f"""\
1390 | import filter{{
1391 | { indent2(filter4i," ") }
1392 | }};
1393 | """)
1394 | if filter4e != "":
1395 | channel4 += textwrap.dedent(f"""\
1396 | export filter{{
1397 | { indent2(filter4e," ") }
1398 | }};
1399 | """)
1400 | if filter6i != "":
1401 | channel6 += textwrap.dedent(f"""\
1402 | import filter{{
1403 | { indent2(filter6i," ") }
1404 | }};
1405 | """)
1406 | if filter6e != "":
1407 | channel6 += textwrap.dedent(f"""\
1408 | export filter{{
1409 | { indent2(filter6e," ") }
1410 | }};
1411 | """)
1412 | if channel4 != "":
1413 | channel4 = textwrap.dedent(f"""\
1414 | ipv4 {{
1415 | { indent2(channel4," ") }
1416 | }};
1417 | """)
1418 | channel4 = indent2(channel4," ")
1419 | if channel6 != "":
1420 | channel6 = textwrap.dedent(f"""\
1421 | ipv6 {{
1422 | { indent2(channel6," ") }
1423 | }};
1424 | """)
1425 | channel6 = indent2(channel6," ")
1426 |
1427 | if MP_BGP == True:
1428 | if birdPeerV6 != None:
1429 | birdconf += textwrap.dedent(f"""\
1430 | protocol bgp dn42_{peerName}_v6 from dnpeers {{
1431 | source address {birdMyV6};
1432 | neighbor {birdPeerV6} % '{if_name}' as {peerASN};
1433 | {channel4}
1434 | {channel6}
1435 | }};
1436 | """)
1437 | else:
1438 | if birdPeerV4 != None:
1439 | birdconf += textwrap.dedent(f"""\
1440 | protocol bgp dn42_{peerName}_v4 from dnpeers {{
1441 | source address {birdMyV4};
1442 | neighbor {birdPeerV4} % '{if_name}' as {peerASN};
1443 | {channel4}
1444 | ipv6 {{
1445 | import none;
1446 | export none;
1447 | }};
1448 | }};
1449 | """)
1450 | if birdPeerV6 != None:
1451 | birdconf += textwrap.dedent(f"""\
1452 | protocol bgp dn42_{peerName}_v6 from dnpeers {{
1453 | source address {birdMyV6};
1454 | neighbor {birdPeerV6} % '{if_name}' as {peerASN};
1455 | ipv4 {{
1456 | import none;
1457 | export none;
1458 | }};
1459 | {channel6}
1460 | }};
1461 | """)
1462 |
1463 | paramaters["peerName"] = peerName
1464 | paramaters_save = { valid_key: paramaters[valid_key] for valid_key in client_valid_keys if valid_key in paramaters}
1465 | paramaters_save["peer_signature"] = ""
1466 | paramaters_save["peer_plaintext"] = ""
1467 | paramaters_save["peerName"] = peerName
1468 |
1469 | devsh = "\n".join([ "#!/bin/bash" , wgsh , setupsh])
1470 | retconfig = {
1471 | f"{wgconfpath}/{peerName}.conf": wgconf,
1472 | f"{wgconfpath}/peerinfo/{peerID}.yaml": yaml.dump(paramaters_save),
1473 | f"{bdconfpath}/{peerName}.conf": birdconf,
1474 | f"{wgconfpath}/{peerName}.sh": devsh,
1475 | }
1476 |
1477 | if customDevice != None:
1478 | devsh = "\n".join([ "#!/bin/bash" , customDeviceSetup , setupsh])
1479 | del retconfig[f"{wgconfpath}/{peerName}.conf"]
1480 | retconfig[f"{wgconfpath}/{peerName}.sh"] = devsh
1481 |
1482 | return {
1483 | "config":retconfig,
1484 | "if_name": if_name,
1485 | "peerName": peerName,
1486 | "paramaters": paramaters,
1487 | "paramaters_save": paramaters_save,
1488 | "successdisplay": successdisplay,
1489 | }
1490 |
1491 | def initDevice():
1492 | print_and_exec(f"ip link add dn42-dummy type dummy")
1493 | print_and_exec(f"ip link set up dn42-dummy")
1494 | print_and_exec(f'ip addr add { my_paramaters["myIPV4"] } dev dn42-dummy')
1495 | print_and_exec(f'ip addr add { my_paramaters["myIPV6"] } dev dn42-dummy')
1496 | for thesh in filter(lambda x:x[-3:] == ".sh", sorted(os.listdir(wgconfpath))):
1497 | print_and_exec(wgconfpath + "/" + thesh)
1498 | def syncWG():
1499 | interval = my_config["reset_wgconf_interval"]
1500 | print("Sync WG interval:",interval)
1501 | if interval <= 0:
1502 | return
1503 | conf_dir = wgconfpath + "/peerinfo"
1504 | while True:
1505 | time.sleep(interval)
1506 | if os.path.isdir(conf_dir): #Check this node hasn't peer with us before
1507 | for conf_file in sorted(os.listdir(conf_dir)):
1508 | if conf_file.endswith(".yaml") and os.path.isfile(f"{conf_dir}/{conf_file}"):
1509 | conf = yaml.load(open(f"{conf_dir}/{conf_file}").read(),Loader=yaml.SafeLoader)
1510 | if conf["customDevice"] != None:
1511 | continue
1512 | if conf["peerWG_Pub_Key"] == None:
1513 | continue
1514 | if conf["peerHost"] == None:
1515 | continue
1516 | ifname = "dn42-" + conf["peerName"]
1517 | peerpubkey = conf["peerWG_Pub_Key"]
1518 | peerendpoint = conf["peerHost"]
1519 | print_and_exec(f"wg set {shlex.quote(ifname)} peer {shlex.quote(peerpubkey)} endpoint {shlex.quote(peerendpoint)}")
1520 |
1521 | def print_and_exec(command):
1522 | if use_remote_command != "":
1523 | command = f'echo {shlex.quote(command + "; exit")} | nc {use_remote_command}'
1524 | print(command)
1525 | os.system(command)
1526 | time.sleep(1)
1527 |
1528 | def print_and_rm(file):
1529 | print("rm " + file)
1530 | try:
1531 | os.remove(file)
1532 | except Exception as e:
1533 | print(e)
1534 |
1535 | def print_and_rmrf(tree):
1536 | print("rm -rf " + tree)
1537 | shutil.rmtree(tree)
1538 |
1539 | def saveConfig(new_config,sync=True):
1540 | if sync:
1541 | RRstate_repo.pull()
1542 | runsh = False
1543 | for path,content in new_config["config"].items():
1544 | print("================================")
1545 | print(path)
1546 | print(content)
1547 | fileparent = pathlib.Path(path).parent.absolute()
1548 | if not os.path.isdir(fileparent):
1549 | os.makedirs(fileparent, mode=0o700 , exist_ok=True)
1550 | with open(path,"w") as conffd:
1551 | conffd.write(content)
1552 | if content.startswith("#!"):
1553 | os.chmod(path, 0o755)
1554 | runsh = True
1555 | print("================================")
1556 | peerName = new_config["peerName"]
1557 | if sync:
1558 | RRstate_repo.push(f'{node_name} peer add {peerName}')
1559 | if runsh:
1560 | print_and_exec(f"{wgconfpath}/{peerName}.sh")
1561 | print_and_exec("birdc configure")
1562 | return None
1563 |
1564 | def deleteConfig(peerID,peerName,deleteDevice=True,sync=True):
1565 | if sync:
1566 | RRstate_repo.pull()
1567 | print_and_rm(f"{wgconfpath}/{peerName}.conf")
1568 | print_and_rm(f"{wgconfpath}/{peerName}.sh")
1569 | print_and_rm(f"{wgconfpath}/peerinfo/{peerID}.yaml")
1570 | print_and_rm(f"{bdconfpath}/{peerName}.conf")
1571 | if sync:
1572 | RRstate_repo.push(f'{node_name} peer del {peerName}')
1573 | if deleteDevice:
1574 | if_name = "dn42-" + peerName
1575 | print_and_exec(f"ip link del {if_name}")
1576 | print_and_exec("birdc configure")
1577 | return None
1578 |
1579 | def updateConfig(peerID,peerName,new_config,deleteDevice=True,sync=True):
1580 | if sync:
1581 | RRstate_repo.pull()
1582 | deleteConfig(peerID,peerName,deleteDevice=deleteDevice,sync=False)
1583 | saveConfig(new_config,sync=False)
1584 | if sync:
1585 | RRstate_repo.push(f'{node_name} peer update {peerName}')
1586 |
1587 | def get_key_default(Dictn,key,default):
1588 | if key in Dictn and Dictn[key] != "" and Dictn[key] != None:
1589 | ValType = type(default)
1590 | if ValType == bool and type(Dictn[key]) == str:
1591 | return Dictn[key].lower() == "true" or Dictn[key].lower() == "on"
1592 | if (ValType == dict or ValType == list) and type(Dictn[key]) == str:
1593 | return json.loads(type(Dictn[key]))
1594 | elif default != None:
1595 | return ValType(Dictn[key])
1596 | else:
1597 | return Dictn[key]
1598 | return default
1599 |
1600 | def qsd2d(qsd):
1601 | return {k:v[0] for k,v in qsd.items()}
1602 |
1603 | def isFormTrue(inp):
1604 | if inp == "on" or inp == "True" or inp == True:
1605 | return True
1606 | return False
1607 |
1608 | def try_get_param(peerID,key,default=""):
1609 | try:
1610 | peerInfo = yaml.load(open(wgconfpath + "/peerinfo/" + str(peerID) + ".yaml").read(),Loader=yaml.SafeLoader)
1611 | except FileNotFoundError as e:
1612 | return default
1613 | if key in peerInfo:
1614 | return peerInfo[key]
1615 | return default
1616 |
1617 | def get_paramaters(paramaters,default_params=my_paramaters,isAdmin=False):
1618 | action = get_key_default(paramaters,"action","OK")
1619 | paramaters = { valid_key: paramaters[valid_key] for valid_key in client_valid_keys if (valid_key in paramaters) }
1620 | if not isAdmin:
1621 | for k in client_valid_keys_admin_only:
1622 | if k in paramaters:
1623 | del paramaters[k]
1624 | paramaters["peer_plaintext"] = get_key_default(paramaters,"peer_plaintext","")
1625 | paramaters["peer_pub_key_pgp"] = get_key_default(paramaters,"peer_pub_key_pgp","")
1626 | paramaters["peer_signature"] = get_key_default(paramaters,"peer_signature","")
1627 | paramaters["peerASN"] = get_key_default(paramaters,"peerASN",None)
1628 | paramaters["hasIPV4"] = get_key_default(paramaters,"hasIPV4",False)
1629 | paramaters["peerIPV4"] = get_key_default(paramaters,"peerIPV4",None)
1630 | paramaters["hasIPV4LL"] = get_key_default(paramaters,"hasIPV4LL",False)
1631 | paramaters["peerIPV4LL"] = get_key_default(paramaters,"peerIPV4LL",None)
1632 | paramaters["hasIPV6"] = get_key_default(paramaters,"hasIPV6",False)
1633 | paramaters["peerIPV6"] = get_key_default(paramaters,"peerIPV6",None)
1634 | paramaters["hasIPV6LL"] = get_key_default(paramaters,"hasIPV6LL",False)
1635 | paramaters["peerIPV6LL"] = get_key_default(paramaters,"peerIPV6LL",None)
1636 | paramaters["myIPV4"] = get_key_default(paramaters,"myIPV4",default_params["myIPV4"]) if default_params["myIPV4"] != "" else ""
1637 | paramaters["myIPV6"] = get_key_default(paramaters,"myIPV6",default_params["myIPV6"]) if default_params["myIPV6"] != "" else ""
1638 | paramaters["myIPV4LL"] = get_key_default(paramaters,"myIPV4LL",default_params["myIPV4LL"]) if default_params["myIPV4LL"] != "" else ""
1639 | paramaters["myIPV6LL"] = get_key_default(paramaters,"myIPV6LL",default_params["myIPV6LL"]) if default_params["myIPV6LL"] != "" else ""
1640 | paramaters["myWG_Pri_Key"] = get_key_default(paramaters,"myWG_Pri_Key",my_config["myWG_Pri_Key"])
1641 | paramaters["myWG_MTU"] = get_key_default(paramaters,"myWG_MTU",1280)
1642 | paramaters["transitMode"] = get_key_default(paramaters,"transitMode","Regular")
1643 | paramaters["customDevice"] = get_key_default(paramaters,"customDevice",None)
1644 | paramaters["customDeviceSetup"]= get_key_default(paramaters,"customDeviceSetup","")
1645 | paramaters["MP_BGP"] = get_key_default(paramaters,"MP_BGP",False)
1646 | paramaters["Ext_Nh"] = get_key_default(paramaters,"Ext_Nh",False)
1647 | paramaters["hasHost"] = get_key_default(paramaters,"hasHost",False)
1648 | paramaters["peerHost"] = get_key_default(paramaters,"peerHost",None)
1649 | paramaters["peerWG_Pub_Key"] = get_key_default(paramaters,"peerWG_Pub_Key","")
1650 | paramaters["peerWG_PS_Key"] = get_key_default(paramaters,"peerWG_PS_Key","")
1651 | paramaters["peerContact"] = get_key_default(paramaters,"peerContact","")
1652 | paramaters["peerName"] = get_key_default(paramaters,"peerName",None)
1653 | paramaters["PeerID"] = get_key_default(paramaters,"PeerID",None)
1654 | paramaters["birdAddConf"] = get_key_default(paramaters,"birdAddConf",{})
1655 |
1656 | paramaters["myWG_Pub_Key"] = wgpri2pub(paramaters["myWG_Pri_Key"])
1657 | #print(yaml.safe_dump(paramaters))
1658 | paramaters = {**default_params,**paramaters}
1659 | return action , paramaters
1660 |
1661 | def remove_sensitive(paramaters):
1662 | ret = {}
1663 | for k,v in paramaters.items():
1664 | if k in client_valid_keys_admin_only:
1665 | continue
1666 | ret[k] = v
1667 | return ret
1668 |
1669 | async def action(paramaters):
1670 | action , paramaters = get_paramaters(paramaters)
1671 | try:
1672 | try:
1673 | if paramaters["PeerID"] != None:
1674 | if int(paramaters["PeerID"]) < 0:
1675 | raise ValueError("Invalid PeerID")
1676 | except Exception as e:
1677 | filename = paramaters["PeerID"] + ".yaml"
1678 | paramaters["PeerID"] = None
1679 | raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), filename)
1680 | if action=="OK":
1681 | if paramaters["peerASN"] == None:
1682 | paramaters["hasIPV4"] = True
1683 | paramaters["hasIPV6LL"] = True
1684 | paramaters["MP_BGP"] = True
1685 | paramaters["Ext_Nh"] = False
1686 | paramaters["hasHost"] = True
1687 | else:
1688 | try:
1689 | peerInfo = yaml.load(open(wgconfpath + "/peerinfo/" + paramaters["PeerID"] + ".yaml").read(),Loader=yaml.SafeLoader)
1690 | _, peerInfo = get_paramaters(peerInfo,isAdmin=True)
1691 | paramaters["myWG_Pub_Key"] = peerInfo["myWG_Pub_Key"]
1692 | except Exception as e:
1693 | pass
1694 | return 200, await get_html(paramaters,action=action,peerSuccess=False)
1695 | if action == "Show":
1696 | if paramaters["PeerID"] == None:
1697 | raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), "")
1698 | try:
1699 | peerInfo = yaml.load(open(wgconfpath + "/peerinfo/" + paramaters["PeerID"] + ".yaml").read(),Loader=yaml.SafeLoader)
1700 | except FileNotFoundError as e:
1701 | e.filename = paramaters["PeerID"] + ".yaml"
1702 | raise e
1703 | peerInfo = { valid_key: peerInfo[valid_key] for valid_key in client_valid_keys if valid_key in peerInfo }
1704 | _, peerInfo = get_paramaters(peerInfo,isAdmin=True)
1705 | if bool(paramaters["peer_plaintext"]):
1706 | del peerInfo["peer_plaintext"]
1707 | if bool(paramaters["peer_pub_key_pgp"]):
1708 | del peerInfo["peer_pub_key_pgp"]
1709 | if bool(paramaters["peer_signature"]):
1710 | del peerInfo["peer_signature"]
1711 | paramaters = {**paramaters,**peerInfo}
1712 | return 200, await get_html(paramaters,action=action,peerSuccess=True)
1713 | # Check ASN is valid for following action
1714 | if paramaters["peerASN"] == None:
1715 | raise ValueError("peerASN can't be null.")
1716 | if paramaters["peerASN"].startswith("AS"):
1717 | check_num = int(paramaters['peerASN'][2:])
1718 | else:
1719 | check_num = int(paramaters['peerASN'])
1720 | paramaters["peerASN"] = "AS" + paramaters["peerASN"]
1721 | #Actions need ASN
1722 | if action=="Delete" or action=="Update":
1723 | mntner = await verify_user_signature(paramaters["peerASN"],paramaters["peer_plaintext"],paramaters["peer_pub_key_pgp"],paramaters["peer_signature"])
1724 | if paramaters["peerHost"] == peerHostDisplayText:
1725 | paramaters["peerHost"] = try_get_param(paramaters["PeerID"],"peerHost","")
1726 | try:
1727 | peerInfo = yaml.load(open(wgconfpath + "/peerinfo/" + paramaters["PeerID"] + ".yaml").read(),Loader=yaml.SafeLoader)
1728 | except FileNotFoundError as e:
1729 | e.filename = paramaters["PeerID"] + ".yaml"
1730 | raise e
1731 | if peerInfo["peerASN"] != paramaters["peerASN"]:
1732 | raise PermissionError("Peer ASN not match")
1733 | if action=="Delete":
1734 | deleteConfig(peerInfo["PeerID"],peerInfo["peerName"],deleteDevice=peerInfo["customDevice"]==None)
1735 | paramaters["PeerID"] = None
1736 | return 200, get_err_page(paramaters,"Profile deleted:" ,yaml.dump(remove_sensitive(peerInfo),sort_keys=False).replace("\n"," "),big_title="Success!")
1737 | elif action=="Update":
1738 | del paramaters["myWG_Pri_Key"]
1739 | del paramaters["myWG_Pub_Key"]
1740 | _, peerInfo = get_paramaters(peerInfo,isAdmin=True)
1741 | paramaters_in = { valid_key: paramaters[valid_key] for valid_key in client_valid_keys if (valid_key in paramaters) }
1742 | for k in client_valid_keys_admin_only:
1743 | if k in paramaters_in:
1744 | del paramaters_in[k]
1745 | paramaters = {**peerInfo,**paramaters_in}
1746 | paramaters = await check_reg_paramater(paramaters,skip_check=paramaters["PeerID"])
1747 | new_config = newConfig(paramaters,overwrite=True)
1748 | if mntner == my_config["admin_mnt"]:
1749 | paramaters["peer_pub_key_pgp"] = ""
1750 | updateConfig(peerInfo["PeerID"],peerInfo["peerName"],new_config,deleteDevice=peerInfo["customDevice"]==None,sync=True)
1751 | return 200, get_err_page(paramaters,"Profile updated:" ,yaml.dump(remove_sensitive(new_config["paramaters_save"]),sort_keys=False).replace("\n"," "),big_title="Success!")
1752 | elif action=="Get Signature":
1753 | return 200, await get_signature_html(dn42repo_base,paramaters)
1754 | elif action == "Register":
1755 | mntner = await verify_user_signature(paramaters["peerASN"],paramaters["peer_plaintext"],paramaters["peer_pub_key_pgp"],paramaters["peer_signature"])
1756 | if mntner not in my_config["admin_mnt"]:
1757 | paramaters["PeerID"] = None
1758 | if my_config["registerAdminOnly"]:
1759 | raise PermissionError("Guest registration is not enabled at this node, please contact admin.")
1760 | paramaters = await check_reg_paramater(paramaters)
1761 | new_config = newConfig(paramaters)
1762 | if mntner == my_config["admin_mnt"]:
1763 | paramaters["peer_pub_key_pgp"] = ""
1764 | paramaters = new_config["paramaters"]
1765 | saveConfig(new_config)
1766 | if paramaters["myHost"] == None:
1767 | myHostDisplay = paramaters["myHostDisplay"]
1768 | else:
1769 | myHostDisplay = paramaters["myHost"] + ":" + str(paramaters["PeerID"])
1770 | myInfo = { **new_config["successdisplay"] ,
1771 | "Endpoint Address":myHostDisplay,
1772 | "My WG Public Key":paramaters["myWG_Pub_Key"],
1773 | "My Contact": paramaters["myContact"]
1774 | }
1775 | return 200, get_err_page(paramaters, f'Your PeerID is: { paramaters["PeerID"] } My info:',yaml.dump(remove_sensitive(myInfo), sort_keys=False),big_title="Peer Success!", tab_title = "Success!",redirect=my_config["register_redirect"])
1776 | return 400, get_err_page(paramaters,"400 - Bad Request",ValueError("Unknow action" + str(action)))
1777 | except Exception as e:
1778 | title = type(e).__name__
1779 | errcode = 400
1780 | if type(e) == FileNotFoundError:
1781 | title = "404 - File or directory not found."
1782 | errorcode = 404
1783 | e = "The resource you are looking for might have been removed, had its name changed, or is temporarily unavailable.\n " + str(e.filename)
1784 | #return errcode, get_err_page(paramaters,title,traceback.format_exc())
1785 | print(traceback.format_exc())
1786 | return errcode, get_err_page(paramaters,title,e)
1787 |
1788 | ipv4s = [ipaddress.ip_network("0.0.0.0/0")]
1789 | ipv6s = [ipaddress.ip_network("::/0")]
1790 |
1791 |
1792 | def get_ip(r):
1793 | rip = ipaddress.ip_address( r.remote_ip )
1794 | if type(rip) == ipaddress.IPv4Address:
1795 | clist = ipv4s
1796 | else:
1797 | clist = ipv6s
1798 | for c in clist:
1799 | if rip in c and "CF-Connecting-IP" in r.headers:
1800 | return r.headers["CF-Connecting-IP"]
1801 | return r.remote_ip
1802 |
1803 |
1804 | class actionHandler(tornado.web.RequestHandler):
1805 | def __init__(self, *args, **kwargs):
1806 | super(actionHandler, self).__init__(*args, **kwargs)
1807 | def set_default_headers(self, *args, **kwargs):
1808 | # Just for fun, pretend I am a php server
1809 | self.set_header('server','Microsoft-IIS/7.5')
1810 | self.set_header('x-powered-by','PHP/5.4.2')
1811 | async def get(self, *args, **kwargs):
1812 | paramaters = { k: self.get_argument(k) for k in self.request.arguments }
1813 | print("GET " + self.request.uri + f' ({get_ip(self.request)}) ' , paramaters)
1814 | code, ret = await action(paramaters)
1815 | self.set_status(code)
1816 | self.write(ret)
1817 | async def post(self, *args, **kwargs):
1818 | paramaters = { k: self.get_argument(k) for k in self.request.arguments }
1819 | print("POST " + self.request.uri + f' ({get_ip(self.request)}) ' , paramaters)
1820 | code, ret = await action(paramaters)
1821 | self.set_status(code)
1822 | self.write(ret)
1823 |
1824 | nfpage = """
1825 |
1826 |
1827 |
1828 |
1829 | 404 - File or directory not found.
1830 |
1843 |
1844 |
1845 |
Server Error
1846 |
1847 |
1851 |
1852 |
1853 |
1854 | """
1855 |
1856 | class My404Handler(tornado.web.RequestHandler):
1857 | # Override prepare() instead of get() to cover all possible HTTP methods.
1858 | def prepare(self):
1859 | self.set_status(404)
1860 | self.write(nfpage)
1861 | def post(self, *args, **kwargs):
1862 | pass
1863 | def get(self, *args, **kwargs):
1864 | pass
1865 |
1866 | if __name__ == '__main__':
1867 | try:
1868 | ipv4s = [ipaddress.ip_network(n) for n in requests.get("https://www.cloudflare.com/ips-v4", verify=False, timeout=3).text.split("\n")]
1869 | ipv6s = [ipaddress.ip_network(n) for n in requests.get("https://www.cloudflare.com/ips-v6", verify=False, timeout=3).text.split("\n")]
1870 | except Exception as e:
1871 | print(traceback.format_exc())
1872 | if my_config["urlprefix"] == "" or my_config["urlprefix"] == "/":
1873 | url_prefix = ""
1874 | url_prefix_pre = ""
1875 | elif my_config["urlprefix"][-1] == "/":
1876 | url_prefix = my_config["urlprefix"]
1877 | url_prefix_pre = my_config["urlprefix"][:-1]
1878 | else:
1879 | url_prefix = my_config["urlprefix"] + "/"
1880 | url_prefix_pre = my_config["urlprefix"]
1881 | app = tornado.web.Application(handlers=[
1882 | ('/' + url_prefix, actionHandler),
1883 | ('/' + url_prefix + 'action_page.php', actionHandler),
1884 | ('/' + url_prefix_pre, tornado.web.RedirectHandler, {"url": url_prefix}),
1885 | ('/' + url_prefix_pre, tornado.web.RedirectHandler, {"url": url_prefix}),
1886 | ('/', tornado.web.RedirectHandler, {"url": url_prefix}),
1887 | (r"(.*)", My404Handler),
1888 | ])
1889 | if my_config["init_device"] == True:
1890 | initDevice()
1891 | syncwg = multiprocessing.Process(target=syncWG, args=())
1892 | syncwg.start()
1893 | server = tornado.httpserver.HTTPServer(app, ssl_options=my_config["ssl_options"] )
1894 | server.listen(my_config["listen_port"],my_config["listen_host"])
1895 | print("Done. Start serving http(s) on " + my_config["listen_host"]+ ":" + str(my_config["listen_port"]))
1896 | tornado.ioloop.IOLoop.current().start()
1897 |
--------------------------------------------------------------------------------
/DN42GIT.py:
--------------------------------------------------------------------------------
1 | from git import Repo
2 |
3 | class DN42GIT():
4 | def __init__(self, gitpath):
5 | if gitpath != None:
6 | self.repo = Repo(gitpath)
7 | else:
8 | self.repo = None
9 | def pull(self):
10 | if self.repo != None:
11 | self.repo.remotes.origin.pull()
12 | def push(self,msg):
13 | if self.repo != None:
14 | self.repo.git.add(all=True)
15 | self.repo.index.commit(msg)
16 | self.repo.remotes.origin.push()
--------------------------------------------------------------------------------
/DN42whois.py:
--------------------------------------------------------------------------------
1 | import re
2 | import os
3 | import time
4 | import errno
5 | import asyncio
6 | import tornado
7 | import traceback
8 | from git import Repo
9 | import ipaddress
10 | from tornado.httpclient import HTTPClientError
11 | from urllib.parse import urlparse
12 | class whois():
13 | def __init__(self, proto, url):
14 | self.proto = proto
15 | if proto == "tcp":
16 | self.whois = tcp_whois(url)
17 | elif proto == "git":
18 | self.whois = git_whois(url,"dn42data",600)
19 | elif proto == "http" or proto == "https":
20 | if not url.startswith("http"):
21 | url = proto + "://" + url
22 | self.whois = http_whois(url)
23 | else:
24 | raise Exception("Support tcp or git only")
25 | async def query(self,query):
26 | return await self.whois.query(query)
27 |
28 | prefixes = {"as-block","as-set","aut-num","dns","inet6num","inetnum","key-cert","mntner","organisation","person","registry","role","route","route6","route-set","schema","tinc-key"}
29 |
30 | def remove_prefix(query):
31 | if "/" in query:
32 | prefix, body = query.split("/",1)
33 | if prefix in prefixes:
34 | query = body
35 | return query
36 |
37 | ngc = """Copy from https://lantian.pub/article/modify-website/serve-dn42-whois-with-nginx.lantian
38 | rewrite "^/([0-9]{1})$" /aut-num/AS424242000$1 last;
39 | rewrite "^/([0-9]{2})$" /aut-num/AS42424200$1 last;
40 | rewrite "^/([0-9]{3})$" /aut-num/AS4242420$1 last;
41 | rewrite "^/([0-9]{4})$" /aut-num/AS424242$1 last;
42 | rewrite "^/([Aa][Ss]|)([0-9]+)$" /aut-num/AS$2 last;
43 | rewrite "^/([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)[/_]([0-9]+)$" /inet_route/$1.$2.$3.$4_$5 last;
44 | rewrite "^/([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$" /inet_route/$1.$2.$3.$4_32 last;
45 | rewrite "^/([0-9a-fA-F:]+)[/_]([0-9]+)$" /inet_route6/$1_$2 last;
46 | rewrite "^/([0-9a-fA-F:]+)$" /inet_route6/$1_128 last;
47 | rewrite "^/([^/]+)-([Dd][Nn]42)$" /person/$1-DN42 last;
48 | rewrite "^/([^/]+)-([Nn][Ee][Oo][Nn][Ee][Tt][Ww][Oo][Rr][Kk])$" /person/$1-NEONETWORK last;
49 | rewrite "^/([^/]+)-([Mm][Nn][Tt])$" /mntner/$1-MNT last;
50 | rewrite "^/([^/]+)-([Ss][Cc][Hh][Ee][Mm][Aa])$" /schema/$1-SCHEMA last;
51 | rewrite "^/([Oo][Rr][Gg])-(.+)$" /organisation/ORG-$2 last;
52 | rewrite "^/([Ss][Ee][Tt])-(.+)-([Tt][Ii][Nn][Cc])$" /tinc-keyset/SET-$2-TINC last;
53 | rewrite "^/([^/]+)-([Tt][Ii][Nn][Cc])$" /tinc-key/$1-TINC last;
54 | rewrite "^/([Rr][Ss])-(.+)$" /route-set/RS-$2 last;
55 | rewrite "^/([Aa][Ss])([0-9]+)-([Aa][Ss])([0-9]+)$" /as-block/$1$2-$3$4 last;
56 | rewrite "^/[Aa][Ss](.+)$" /as-set/AS$1 last;
57 | rewrite "^/[Pp][Gg][Pp][Kk][Ee][Yy][-](.+)$" /key-cert/PGPKEY-$1 last;
58 | rewrite "^/([^/]+)$" /dns/$1 last;"""
59 |
60 | def add_prefix(query):
61 | if "/" in query:
62 | prefix, body = query.split("/",1)
63 | if prefix in prefixes:
64 | return query
65 | for c in filter(lambda x:"rewrite" in x, ngc.split("\n")):
66 | matches = re.finditer(r"\"(.*)\" (.*) last", c, re.MULTILINE)
67 | sub_re, replace_pattern = list(matches)[0].groups()
68 | sub_re = sub_re[0] + sub_re[2:]
69 | required_group_num = max( map( lambda x:int(x.groups()[0]), re.finditer(r"\$(\d+)", replace_pattern, re.MULTILINE)))
70 | sub_match = list( map( lambda x:x.groups(), re.finditer(sub_re, query, re.MULTILINE) ))
71 | sub_matches = []
72 | if len(sub_match) == 1:
73 | sub_matches = sub_match[0]
74 | if len(sub_matches) >= required_group_num:
75 | for i in range(required_group_num):
76 | replace_pattern = replace_pattern.replace("$" + str(i+1),sub_matches[i])
77 | return replace_pattern[1:]
78 |
79 | def proc_data(data_in):
80 | ret_dict = {}
81 | for data_in_item in data_in.split("\n"):
82 | if len(data_in_item) == 0:
83 | continue
84 | if data_in_item[0] == "%":
85 | continue
86 | if ":" not in data_in_item:
87 | continue
88 | key , val = data_in_item.split(":",1)
89 | val = val.lstrip()
90 | if key in ret_dict:
91 | ret_dict[key] = [val] + ret_dict[key]
92 | else:
93 | ret_dict[key] = [val]
94 | return ret_dict
95 |
96 | class tcp_whois():
97 | def __init__(self, url):
98 | self.host, self.port = url.rsplit(":",1)
99 | self.port = int(self.port)
100 | async def query(self,query):
101 | query = remove_prefix(query)
102 | return await self.socket_query(query)
103 | async def socket_query(self,query):
104 | query = query.strip()
105 | reader, writer = await asyncio.open_connection(self.host, self.port)
106 | writer.write(query.encode("utf8"))
107 | writer.write("\n".encode("utf8"))
108 | await writer.drain()
109 | writer.write_eof()
110 | data = await reader.read()
111 | writer.close()
112 | await writer.wait_closed()
113 | result = data.decode("utf8")
114 | result_item = result.split("\n")
115 | result_item = list(filter(lambda l:not l.startswith("%") and ":" in l,result_item))
116 | if len(result_item) == 0:
117 | raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), query)
118 | return "\n".join(result_item)
119 |
120 | class git_whois():
121 | def __init__(self, url, local_git,pull_cooldown):
122 | os.environ['GIT_SSH_COMMAND'] = "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
123 | if not os.path.isdir(local_git):
124 | self.repo = Repo.clone_from(url, local_git,depth=1,env={'GIT_SSL_NO_VERIFY': '1'},config='http.sslVerify=false')
125 | else:
126 | self.repo = Repo(local_git)
127 | self.repo.remotes.origin.set_url(url)
128 | self.cooldown = pull_cooldown
129 | self.pulltime = time.time()
130 | self.local_git = local_git
131 | self.repo.remotes.origin.pull()
132 | async def query(self,query):
133 | query = query.strip()
134 | if time.time() - self.pulltime > self.cooldown:
135 | print(self.repo.remotes.origin)
136 | self.repo.remotes.origin.pull()
137 | self.pulltime = time.time()
138 | query = add_prefix(query)
139 | if query.startswith("inetnum/") or query.startswith("inet6num/") or query.startswith("route/") or query.startswith("route6/") or query.startswith("inet_route/") or query.startswith("inet_route6/"):
140 | if query.startswith("inetnum/") or query.startswith("route/") or query.startswith("inet_route/"):
141 | max_length=32
142 | elif query.startswith("inet6num/") or query.startswith("route6/") or query.startswith("inet_route6/"):
143 | max_length=128
144 | prefix,body = query.split("/",1)
145 | ip,length = ("",0)
146 | if "_" in body:
147 | ip,length = body.split("_")
148 | length = int(length)
149 | elif "/" in body:
150 | ip,length = body.split("/")
151 | length = int(length)
152 | else:
153 | ip = body
154 | length = max_length
155 | if length > max_length:
156 | raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), query)
157 | ret_result = ""
158 | inet_route_result = {"inet":False,"route":False}
159 | for i in range(length,-1,-1):
160 | try:
161 | try_net = ipaddress.ip_network(ip + "/" + str(i),strict=False)
162 | if prefix not in ("inet_route","inet_route6"):
163 | return self.file_query(prefix + "/" + str(try_net.network_address) + "_" + str(i))
164 | else:
165 | query_body = str(try_net.network_address) + "_" + str(i)
166 | if prefix == "inet_route":
167 | qi = "inetnum/"
168 | qr = "route/"
169 | elif prefix == "inet_route6":
170 | qi = "inet6num/"
171 | qr = "route6/"
172 | try:
173 | if inet_route_result["inet"] == False:
174 | ret_result += self.file_query(qi + query_body)
175 | inet_route_result["inet"] = True
176 | except FileNotFoundError as e:
177 | pass
178 | try:
179 | if inet_route_result["route"] == False:
180 | ret_result += self.file_query(qr + query_body)
181 | inet_route_result["route"] = True
182 | except FileNotFoundError as e:
183 | pass
184 | if inet_route_result["inet"] == True and inet_route_result["route"] == True:
185 | break
186 | except FileNotFoundError as e:
187 | pass
188 | if ret_result != "":
189 | return ret_result
190 | raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), query)
191 | else:
192 | return self.file_query(query)
193 | def file_query(self,query):
194 | path = os.path.join(self.local_git , "data" , query )
195 | try:
196 | response = open(path,"rb").read()
197 | result = response.decode("utf8")
198 | result_item = result.split("\n")
199 | result_item = list(filter(lambda l:not l.startswith("%") and ":" in l,result_item))
200 | return f"% Information related to '{query}':\n" + "\n".join(result_item) + "\n\n"
201 | except FileNotFoundError:
202 | raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), query)
203 |
204 |
205 | class http_whois():
206 | def __init__(self, url):
207 | if not url.startswith("http"):
208 | raise Exception('URL not startswith "http"')
209 | self.url = url
210 | async def query(self,query):
211 | query = query.strip()
212 | query = add_prefix(query)
213 | if query.startswith("inetnum/") or query.startswith("inet6num/") or query.startswith("route/") or query.startswith("route6/") or query.startswith("inet_route/") or query.startswith("inet_route6/"):
214 | if query.startswith("inetnum/") or query.startswith("route/") or query.startswith("inet_route/"):
215 | max_length=32
216 | elif query.startswith("inet6num/") or query.startswith("route6/") or query.startswith("inet_route6/"):
217 | max_length=128
218 | prefix,body = query.split("/",1)
219 | ip,length = ("",0)
220 | if "_" in body:
221 | ip,length = body.split("_")
222 | length = int(length)
223 | elif "/" in body:
224 | ip,length = body.split("/")
225 | length = int(length)
226 | else:
227 | ip = body
228 | length = max_length
229 | if length > max_length:
230 | raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), query)
231 | ret_result = ""
232 | inet_route_result = {"inet":False,"route":False}
233 | http_prequaries = {}
234 | loop = asyncio.get_event_loop()
235 | for i in range(length,-1,-1):
236 | try_net = ipaddress.ip_network(ip + "/" + str(i),strict=False)
237 | if prefix not in ("inet_route","inet_route6"):
238 | qq = prefix + "/" + str(try_net.network_address) + "_" + str(i)
239 | http_prequaries[qq] = loop.create_task(self.http_query(qq))
240 | else:
241 | query_body = str(try_net.network_address) + "_" + str(i)
242 | if prefix == "inet_route":
243 | qi = "inetnum/"
244 | qr = "route/"
245 | elif prefix == "inet_route6":
246 | qi = "inet6num/"
247 | qr = "route6/"
248 | http_prequaries[qi + query_body] = loop.create_task(self.http_query(qi + query_body))
249 | http_prequaries[qr + query_body] = loop.create_task(self.http_query(qr + query_body))
250 | for i in range(length,-1,-1):
251 | try:
252 | try_net = ipaddress.ip_network(ip + "/" + str(i),strict=False)
253 | if prefix not in ("inet_route","inet_route6"):
254 | return self.file_query(prefix + "/" + str(try_net.network_address) + "_" + str(i))
255 | else:
256 | query_body = str(try_net.network_address) + "_" + str(i)
257 | if prefix == "inet_route":
258 | qi = "inetnum/"
259 | qr = "route/"
260 | elif prefix == "inet_route6":
261 | qi = "inet6num/"
262 | qr = "route6/"
263 | try:
264 | if inet_route_result["inet"] == False:
265 | ret_result += await http_prequaries[qi + query_body]
266 | inet_route_result["inet"] = True
267 | except FileNotFoundError as e:
268 | pass
269 | try:
270 | if inet_route_result["route"] == False:
271 | ret_result += await http_prequaries[qr + query_body]
272 | inet_route_result["route"] = True
273 | except FileNotFoundError as e:
274 | pass
275 | if inet_route_result["inet"] == True and inet_route_result["route"] == True:
276 | break
277 | except FileNotFoundError as e:
278 | pass
279 | for k,q in http_prequaries.items():
280 | q.cancel()
281 | if ret_result != "":
282 | return ret_result
283 | raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), query)
284 | else:
285 | return await self.http_query(query)
286 | async def http_query(self,query):
287 | client = tornado.httpclient.AsyncHTTPClient()
288 | try:
289 | response_f = client.fetch(self.url + "/" + query)
290 | response = await response_f
291 | result = response.body.decode("utf8")
292 | result_item = result.split("\n")
293 | result_item = list(filter(lambda l:not l.startswith("%") and ":" in l,result_item))
294 | return f"% Information related to '{query}':\n" + "\n".join(result_item) + "\n\n"
295 | except asyncio.CancelledError:
296 | response_f.cancel()
297 | client.close()
298 | return ""
299 | except HTTPClientError as e:
300 | if e.code == 404:
301 | raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), query)
302 | raise e
303 |
304 | def get_whois_hendler(my_whois):
305 | async def whois_hendler(reader, writer):
306 | try:
307 | query = b""
308 | while True:
309 | indata = await reader.read(1024)
310 | query += indata
311 | if len(indata) == 0: # connection closed
312 | writer.close()
313 | break
314 | if b"\n" in indata:
315 | try:
316 | print("WHOIS query: " + query.decode().strip())
317 | outdata = await my_whois.query(query.decode())
318 | except Exception as e:
319 | traceback.print_exc()
320 | outdata = "% Not found"
321 | writer.write(outdata.encode())
322 | await writer.drain()
323 | writer.close()
324 | break
325 | except Exception as e:
326 | traceback.print_exc()
327 | return whois_hendler
328 |
329 | async def whois_server():
330 | HOST = '0.0.0.0'
331 | PORT = 43
332 | print('prepareing for whois...')
333 | my_whois = git_whois("https://github.com/KusakabeSi/dn42-registry","whoisdata",600)
334 | #my_whois = http_whois("https://cdn.jsdelivr.net/gh/KusakabeSi/dn42-registry/data")
335 |
336 | whois_hendler = get_whois_hendler(my_whois)
337 | print('server start at: %s:%s' % (HOST, PORT))
338 | server = await asyncio.start_server(whois_hendler,HOST,PORT)
339 | print('wait for connection...')
340 | if __name__ == '__main__':
341 | loop = asyncio.get_event_loop()
342 | asyncio.ensure_future(whois_server())
343 | loop.run_forever()
344 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Peer with me at DN42 network!!
2 |
3 | https://net.whojk.com/
4 |
5 | #### Special thanks to KuskakbeSi
6 |
7 | peer with KuskakbeSi:
8 | https://dn42.kskb.eu.org/
9 |
10 | ## installition
11 |
12 | ```
13 | pip3 install -r requirement.txt
14 | ```
15 |
16 | Then fill your server infomation to ```my_config.json``` and ```my_parameters.json```
17 |
18 | ```
19 | python3 ./DN42AutoPeer.py
20 | ```
21 |
--------------------------------------------------------------------------------
/my_config.yaml:
--------------------------------------------------------------------------------
1 | jwt_secret: null
2 | html_title: DN42 Self-Service Peering Portal
3 | git_repo_url: https://github.com/HuJK/DN42-AutoPeer
4 | listen_host: 0.0.0.0
5 | listen_port: '4242'
6 | ssl_options: null
7 | myWG_Pri_Key: ''
8 | urlprefix: ''
9 | register_redirect: null
10 | wgconfpath: /etc/dn42ap
11 | bdconfpath: /etc/bird/peers
12 | gitsyncpath: null
13 | dn42_whois_server:
14 | - git
15 | - "https://dn42ap:qZ8Z5szMt2RsWbdMyEku@gitlab.com/kskbshi/data/dn42-registry"
16 | dn42repo_base: https://explorer.burble.com/?#/
17 | admin_mnt: ''
18 | DN42_valid_ipv4s:
19 | - 172.20.0.0/14
20 | - 172.31.0.0/16
21 | - 10.0.0.0/8
22 | DN42_valid_ipv6s:
23 | - fd00::/8
24 | valid_ipv4_linklocal: 169.254.42.0/24
25 | valid_ipv6_linklocal: fe80::/64
26 | wg_port_search_range: range(20000 + peerASN % 10000 ,20100 + peerASN % 10000 )
27 | init_device: true
28 | reset_wgconf_interval: 0
29 | myHostHidden: false
30 | peerEndpointHidden: false
31 | registerAdminOnly: false
32 |
--------------------------------------------------------------------------------
/my_parameters.yaml:
--------------------------------------------------------------------------------
1 | myIPV4: ''
2 | myIPV6: ''
3 | myIPV4LL: ''
4 | myIPV6LL: ''
5 | myHost: ''
6 | myHostDisplay: ''
7 | myASN: AS4242420000
8 | myContact: ''
9 | allowExtNh: true
10 |
--------------------------------------------------------------------------------
/requirement.txt:
--------------------------------------------------------------------------------
1 | pycryptodome
2 | gitpython
3 | pyOpenSSL
4 | tornado
5 | pyyaml
6 | pyjwt
7 | PGPy
8 |
--------------------------------------------------------------------------------
/ssh/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore everything
2 | *
3 |
4 | # But not these files...
5 | !.gitignore
--------------------------------------------------------------------------------
/ssl/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore everything
2 | *
3 |
4 | # But not these files...
5 | !.gitignore
--------------------------------------------------------------------------------