lib.rs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. use lazy_static::lazy_static;
  2. use serde::{Deserialize, Serialize};
  3. use sled::Db;
  4. use std::{
  5. collections::{BTreeMap, HashMap, HashSet},
  6. fmt,
  7. fs::File,
  8. io::BufReader,
  9. };
  10. pub mod bridge_verification_info;
  11. pub mod extra_info;
  12. pub mod negative_report;
  13. pub mod positive_report;
  14. use extra_info::*;
  15. use negative_report::*;
  16. use positive_report::*;
  17. #[derive(Debug, Deserialize)]
  18. pub struct Config {
  19. pub db: DbConfig,
  20. require_bridge_token: bool,
  21. }
  22. #[derive(Debug, Deserialize)]
  23. pub struct DbConfig {
  24. // The path for the server database, default is "server_db"
  25. pub db_path: String,
  26. }
  27. impl Default for DbConfig {
  28. fn default() -> DbConfig {
  29. DbConfig {
  30. db_path: "server_db".to_owned(),
  31. }
  32. }
  33. }
  34. lazy_static! {
  35. // known country codes based on Tor geoIP database
  36. // Produced with `cat /usr/share/tor/geoip{,6} | grep -v ^# | grep -o ..$ | sort | uniq | tr '[:upper:]' '[:lower:]' | tr '\n' ',' | sed 's/,/","/g'`
  37. pub static ref COUNTRY_CODES: HashSet<&'static str> = HashSet::from(["??","ad","ae","af","ag","ai","al","am","ao","ap","aq","ar","as","at","au","aw","ax","az","ba","bb","bd","be","bf","bg","bh","bi","bj","bl","bm","bn","bo","bq","br","bs","bt","bv","bw","by","bz","ca","cc","cd","cf","cg","ch","ci","ck","cl","cm","cn","co","cr","cs","cu","cv","cw","cx","cy","cz","de","dj","dk","dm","do","dz","ec","ee","eg","eh","er","es","et","eu","fi","fj","fk","fm","fo","fr","ga","gb","gd","ge","gf","gg","gh","gi","gl","gm","gn","gp","gq","gr","gs","gt","gu","gw","gy","hk","hm","hn","hr","ht","hu","id","ie","il","im","in","io","iq","ir","is","it","je","jm","jo","jp","ke","kg","kh","ki","km","kn","kp","kr","kw","ky","kz","la","lb","lc","li","lk","lr","ls","lt","lu","lv","ly","ma","mc","md","me","mf","mg","mh","mk","ml","mm","mn","mo","mp","mq","mr","ms","mt","mu","mv","mw","mx","my","mz","na","nc","ne","nf","ng","ni","nl","no","np","nr","nu","nz","om","pa","pe","pf","pg","ph","pk","pl","pm","pn","pr","ps","pt","pw","py","qa","re","ro","rs","ru","rw","sa","sb","sc","sd","se","sg","sh","si","sj","sk","sl","sm","sn","so","sr","ss","st","sv","sx","sy","sz","tc","td","tf","tg","th","tj","tk","tl","tm","tn","to","tr","tt","tv","tw","tz","ua","ug","um","us","uy","uz","va","vc","ve","vg","vi","vn","vu","wf","ws","ye","yt","za","zm","zw"]);
  38. // read config data at run time
  39. pub static ref CONFIG: Config = serde_json::from_reader(
  40. BufReader::new(
  41. File::open("config.json").expect("Could not read config file") // TODO: Make config filename configurable
  42. )
  43. ).expect("Reading config file from JSON failed");
  44. }
  45. /// Get Julian date
  46. pub fn get_date() -> u32 {
  47. time::OffsetDateTime::now_utc()
  48. .date()
  49. .to_julian_day()
  50. .try_into()
  51. .unwrap()
  52. }
  53. #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
  54. pub enum BridgeDistributor {
  55. Lox,
  56. }
  57. /// All the info for a bridge, to be stored in the database
  58. #[derive(Serialize, Deserialize)]
  59. pub struct BridgeInfo {
  60. /// hashed fingerprint (SHA-1 hash of 20-byte bridge ID)
  61. pub fingerprint: [u8; 20],
  62. /// nickname of bridge (probably not necessary)
  63. pub nickname: String,
  64. /// first Julian date we started collecting data on this bridge
  65. pub first_seen: u32,
  66. /// flag indicating whether the bridge is believed to be blocked
  67. pub is_blocked: bool,
  68. /// map of dates to data for that day
  69. pub info_by_day: HashMap<u32, DailyBridgeInfo>,
  70. }
  71. impl BridgeInfo {
  72. pub fn new(fingerprint: [u8; 20], nickname: &String) -> Self {
  73. Self {
  74. fingerprint: fingerprint,
  75. nickname: nickname.to_string(),
  76. first_seen: get_date(),
  77. is_blocked: false,
  78. info_by_day: HashMap::<u32, DailyBridgeInfo>::new(),
  79. }
  80. }
  81. }
  82. impl fmt::Display for BridgeInfo {
  83. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  84. let mut str = format!(
  85. "fingerprint:{}\n",
  86. array_bytes::bytes2hex("", self.fingerprint).as_str()
  87. );
  88. str.push_str(format!("nickname: {}\n", self.nickname).as_str());
  89. str.push_str(format!("first_seen: {}\n", self.first_seen).as_str());
  90. str.push_str(format!("is_blocked: {}\n", self.is_blocked).as_str());
  91. str.push_str("info_by_day:");
  92. for day in self.info_by_day.keys() {
  93. str.push_str(format!("\n day: {}", day).as_str());
  94. let daily_info = self.info_by_day.get(day).unwrap();
  95. for line in daily_info.to_string().lines() {
  96. str.push_str(format!("\n {}", line).as_str());
  97. }
  98. }
  99. write!(f, "{}", str)
  100. }
  101. }
  102. #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
  103. pub enum BridgeInfoType {
  104. BridgeIps,
  105. NegativeReports,
  106. PositiveReports,
  107. }
  108. /// Information about bridge reachability, gathered daily
  109. #[derive(Serialize, Deserialize)]
  110. pub struct DailyBridgeInfo {
  111. pub info_by_country: BTreeMap<String, BTreeMap<BridgeInfoType, u32>>,
  112. }
  113. impl DailyBridgeInfo {
  114. pub fn new() -> Self {
  115. Self {
  116. info_by_country: BTreeMap::<String, BTreeMap<BridgeInfoType, u32>>::new(),
  117. }
  118. }
  119. pub fn add_info(
  120. &mut self,
  121. info_type: BridgeInfoType,
  122. count_per_country: &BTreeMap<String, u32>,
  123. ) {
  124. for country in count_per_country.keys() {
  125. if self.info_by_country.contains_key(country) {
  126. let info = self.info_by_country.get_mut(country).unwrap();
  127. if !info.contains_key(&info_type) {
  128. info.insert(
  129. info_type,
  130. *count_per_country.get(&country.to_string()).unwrap(),
  131. );
  132. } else if info_type == BridgeInfoType::BridgeIps {
  133. // Use newest value we've seen today
  134. if info.get(&info_type).unwrap() < count_per_country.get(country).unwrap() {
  135. info.insert(
  136. BridgeInfoType::BridgeIps,
  137. *count_per_country.get(&country.to_string()).unwrap(),
  138. );
  139. }
  140. } else {
  141. let new_count = info.get(&info_type).unwrap()
  142. + *count_per_country.get(&country.to_string()).unwrap();
  143. info.insert(info_type, new_count);
  144. }
  145. } else {
  146. let mut info = BTreeMap::<BridgeInfoType, u32>::new();
  147. info.insert(
  148. info_type,
  149. *count_per_country.get(&country.to_string()).unwrap(),
  150. );
  151. self.info_by_country.insert(country.to_string(), info);
  152. }
  153. }
  154. }
  155. }
  156. impl fmt::Display for DailyBridgeInfo {
  157. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  158. let mut str = String::from("info:");
  159. for country in self.info_by_country.keys() {
  160. let info = self.info_by_country.get(country).unwrap();
  161. let ip_count = match info.get(&BridgeInfoType::BridgeIps) {
  162. Some(v) => v,
  163. None => &0,
  164. };
  165. let nr_count = match info.get(&BridgeInfoType::NegativeReports) {
  166. Some(v) => v,
  167. None => &0,
  168. };
  169. let pr_count = match info.get(&BridgeInfoType::PositiveReports) {
  170. Some(v) => v,
  171. None => &0,
  172. };
  173. if ip_count > &0 || nr_count > &0 || pr_count > &0 {
  174. str.push_str(
  175. format!(
  176. "\n cc: {}\n connections: {}\n negative reports: {}\n positive reports: {}",
  177. country,
  178. ip_count,
  179. nr_count,
  180. pr_count,
  181. )
  182. .as_str(),
  183. );
  184. }
  185. }
  186. write!(f, "{}", str)
  187. }
  188. }
  189. /// Adds the extra-info data for a single bridge to the database. If the
  190. /// database already contains an extra-info for this bridge for thid date,
  191. /// but this extra-info contains different data for some reason, use the
  192. /// greater count of connections from each country.
  193. pub fn add_extra_info_to_db(db: &Db, extra_info: ExtraInfo) {
  194. let fingerprint = extra_info.fingerprint;
  195. let mut bridge_info = match db.get(&fingerprint).unwrap() {
  196. Some(v) => bincode::deserialize(&v).unwrap(),
  197. None => BridgeInfo::new(fingerprint, &extra_info.nickname),
  198. };
  199. // If we already have an entry, compare it with the new one. For each
  200. // country:count mapping, use the greater of the two counts.
  201. if bridge_info.info_by_day.contains_key(&extra_info.date) {
  202. let daily_bridge_info = bridge_info.info_by_day.get_mut(&extra_info.date).unwrap();
  203. daily_bridge_info.add_info(BridgeInfoType::BridgeIps, &extra_info.bridge_ips);
  204. } else {
  205. // No existing entry; make a new one.
  206. let mut daily_bridge_info = DailyBridgeInfo {
  207. info_by_country: BTreeMap::<String, BTreeMap<BridgeInfoType, u32>>::new(),
  208. };
  209. daily_bridge_info.add_info(BridgeInfoType::BridgeIps, &extra_info.bridge_ips);
  210. bridge_info
  211. .info_by_day
  212. .insert(extra_info.date, daily_bridge_info);
  213. }
  214. // Commit changes to database
  215. db.insert(fingerprint, bincode::serialize(&bridge_info).unwrap())
  216. .unwrap();
  217. }