port_forwarding.ncdi 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. include_guard "port_forwarding"
  2. template port_forwarding {
  3. alias("_arg0") forwardings_file;
  4. alias("_arg1") template_forward;
  5. # Map which holds the set of current port forwardings.
  6. # Enties are: {protocol, port_start, port_end, dest_addr}:""
  7. value([]) map;
  8. # Blocker which is initially down and is toggled down-up
  9. # whenever the forwarding change.
  10. blocker() update_blocker;
  11. # Process manager, each forwarding has a port_forwarding__instance process.
  12. # The process identifiers are the same as the keys in the map.
  13. process_manager() mgr;
  14. # Spawn a process for dealing with storage of port forwardings on disk.
  15. spawn("port_forwarding__stored", {});
  16. }
  17. template port_forwarding__instance {
  18. alias("_caller") pf;
  19. alias("_arg0") protocol;
  20. alias("_arg1") port_start;
  21. alias("_arg2") port_end;
  22. alias("_arg3") dest_addr;
  23. log("notice", "adding port forwarding ", protocol, ":", port_start, ":", port_end, " to ", dest_addr);
  24. log_r("notice", "removed port forwarding ", protocol, ":", port_start, ":", port_end, " to ", dest_addr);
  25. # Do the forwarding.
  26. call_with_caller_target(pf.template_forward, {protocol, port_start, port_end, dest_addr}, "pf._caller");
  27. }
  28. template port_forwarding_add {
  29. alias(_arg0) pf;
  30. alias("_arg1") protocol;
  31. alias("_arg2") port_start;
  32. alias("_arg3") port_end;
  33. alias("_arg4") dest_addr;
  34. var("false") succeeded;
  35. var("") error_text;
  36. var("true") not_finished;
  37. backtrack_point() finished_point;
  38. If (not_finished) {
  39. # Check for conflicts with existing forwardings.
  40. Foreach (pf.map.keys As entry) {
  41. value(entry) entry;
  42. entry->get("0") e_protocol;
  43. entry->get("1") e_port_start;
  44. entry->get("2") e_port_end;
  45. val_different(protocol, e_protocol) different_protocol;
  46. num_lesser(port_end, e_port_start) before;
  47. num_greater(port_start, e_port_end) after;
  48. or(different_protocol, before, after) no_conflict;
  49. not(no_conflict) conflict;
  50. If (conflict) {
  51. error_text->set("Port forwarding conflicts with an existing forwarding.");
  52. not_finished->set("false");
  53. finished_point->go();
  54. };
  55. };
  56. # Build entry key.
  57. var({protocol, port_start, port_end, dest_addr}) key;
  58. # Insert to map and toggle blocker.
  59. pf.map->insert(key, "");
  60. pf.update_blocker->downup();
  61. # Start process.
  62. pf.mgr->start(key, "port_forwarding__instance", {protocol, port_start, port_end, dest_addr});
  63. succeeded->set("true");
  64. not_finished->set("false");
  65. finished_point->go();
  66. };
  67. }
  68. template port_forwarding_remove {
  69. alias(_arg0) pf;
  70. alias("_arg1") protocol;
  71. alias("_arg2") port_start;
  72. alias("_arg3") port_end;
  73. alias("_arg4") dest_addr;
  74. var("false") succeeded;
  75. var("") error_text;
  76. var("true") not_finished;
  77. backtrack_point() finished_point;
  78. If (not_finished) {
  79. # Build entry key.
  80. var({protocol, port_start, port_end, dest_addr}) key;
  81. # Check if the forwarding exists.
  82. pf.map->try_get(key) entry;
  83. not(entry.exists) does_not_exist;
  84. If (does_not_exist) {
  85. error_text->set("Port forwarding does not exist.");
  86. not_finished->set("false");
  87. finished_point->go();
  88. };
  89. # Stop process.
  90. pf.mgr->stop(key);
  91. # Remove from map and toggle blocker.
  92. pf.map->remove(key);
  93. pf.update_blocker->downup();
  94. succeeded->set("true");
  95. not_finished->set("false");
  96. finished_point->go();
  97. };
  98. }
  99. template port_forwarding__stored {
  100. alias("_caller") pf;
  101. # Create file if it doesn't exist.
  102. file_stat(pf.forwardings_file) stat;
  103. If (stat.succeeded) { print(); } Else {
  104. file_write(pf.forwardings_file, "{}\n");
  105. };
  106. # Read port forwardings from file.
  107. file_read(pf.forwardings_file) data;
  108. from_string(data) forwardings;
  109. # Add them.
  110. Foreach (forwardings As fwd) {
  111. value(fwd) fwd;
  112. fwd->get("0") protocol;
  113. fwd->get("1") port_start;
  114. fwd->get("2") port_end;
  115. fwd->get("3") dest_addr;
  116. call("port_forwarding_add", {"_caller.pf", protocol, port_start, port_end, dest_addr});
  117. };
  118. # Write forwardings to file on exit.
  119. imperative("<none>", {}, "port_forwarding__write", {}, "6000");
  120. # Also write forwardings whenever they are changed.
  121. pf.update_blocker->use();
  122. call("port_forwarding__write", {});
  123. }
  124. template port_forwarding__write {
  125. alias("_caller.pf") pf;
  126. # Convert forwardings to string.
  127. to_string(pf.map.keys) data;
  128. concat(data, "\n") data;
  129. # Build name of temporary file.
  130. concat(pf.forwardings_file, ".new") temp_file;
  131. # Write temporary file.
  132. file_write(temp_file, data);
  133. # Move to live file.
  134. runonce({"/bin/mv", temp_file, pf.forwardings_file});
  135. }