1
0
Fork 0
mirror of https://github.com/postmannen/ctrl.git synced 2025-03-31 01:24:31 +00:00

added tcp forwarder

This commit is contained in:
postmannen 2025-01-23 19:13:09 +01:00
parent e1a3fdca05
commit 5127ab7099
11 changed files with 1716 additions and 2 deletions

1
.gitignore vendored
View file

@ -17,3 +17,4 @@ doc/concept/via/README.md
notes.txt
toolbox/
signing/
frontend/

301
doc/.$drawings.drawio.bkp Normal file
View file

@ -0,0 +1,301 @@
<mxfile host="Electron" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/24.7.17 Chrome/128.0.6613.36 Electron/32.0.1 Safari/537.36" version="24.7.17" pages="4">
<diagram id="v0fDrbftQkuLOIMYALoA" name="key distribution flow">
<mxGraphModel dx="812" dy="603" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="tMiANJ_N9iX26Bk4R87N-14" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="tMiANJ_N9iX26Bk4R87N-1" target="tMiANJ_N9iX26Bk4R87N-4">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-1" value="node sends public key to central" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="80" y="160" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-16" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="tMiANJ_N9iX26Bk4R87N-2" target="tMiANJ_N9iX26Bk4R87N-11">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-2" value="ask if allowed to be put in central db" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="80" y="344" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-15" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="tMiANJ_N9iX26Bk4R87N-4" target="tMiANJ_N9iX26Bk4R87N-2">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-4" value="store key in not-acked db on central" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="80" y="250" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-18" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="tMiANJ_N9iX26Bk4R87N-5" target="tMiANJ_N9iX26Bk4R87N-12">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-5" value="create hash of all keys in db" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="80" y="520" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-17" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="tMiANJ_N9iX26Bk4R87N-11" target="tMiANJ_N9iX26Bk4R87N-5">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-11" value="operator allows the key onto central db" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="80" y="430" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-12" value="push the db with the hash to all nodes&lt;br&gt;&lt;br&gt;NB: should it be pushed to both ack&#39;ed and not ack&#39;ed nodes ?" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="80" y="620" width="120" height="130" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-21" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="tMiANJ_N9iX26Bk4R87N-20" target="tMiANJ_N9iX26Bk4R87N-1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-20" value="new key&lt;div&gt;detected in hello message&lt;/div&gt;" style="ellipse;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="80" y="50" width="120" height="80" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-31" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="tMiANJ_N9iX26Bk4R87N-22" target="tMiANJ_N9iX26Bk4R87N-23">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-22" value="keys update&lt;div&gt;receive&lt;/div&gt;&lt;div&gt;(node)&lt;/div&gt;" style="ellipse;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="320" y="50" width="120" height="80" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-25" value="yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="tMiANJ_N9iX26Bk4R87N-23" target="tMiANJ_N9iX26Bk4R87N-24">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-27" value="no" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="tMiANJ_N9iX26Bk4R87N-23" target="tMiANJ_N9iX26Bk4R87N-26">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-23" value="if length == 0" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="320" y="160" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-24" value="delete all keys" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="490" y="160" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-29" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="tMiANJ_N9iX26Bk4R87N-26" target="tMiANJ_N9iX26Bk4R87N-28">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-26" value="update and replace all keys" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="320" y="270" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-28" value="save to local file" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="320" y="360" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-30" value="keys allow&lt;div&gt;(central)&lt;/div&gt;" style="ellipse;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="650" y="50" width="120" height="80" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-32" value="get node name from operator to allow" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="650" y="160" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="WOR5zTzofjd4Fm8wHdib-1" value="move the node from the not-acked map to the ack&#39;ed map" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="650" y="270" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="WOR5zTzofjd4Fm8wHdib-2" value="create a new hash of all the nodes with keys in map" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="650" y="360" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="WOR5zTzofjd4Fm8wHdib-3" value="push updated map to all nodes" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="650" y="460" width="120" height="60" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
<diagram id="SR1WNxjGEkNWK4Vj7WTd" name="Page-3">
<mxGraphModel dx="812" dy="603" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
</root>
</mxGraphModel>
</diagram>
<diagram id="Miw9yFcv0oz29-3Sloob" name="portSrc-Dst">
<mxGraphModel dx="1160" dy="861" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="eCXqwsX-LsBgD2zF08TC-32" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="eCXqwsX-LsBgD2zF08TC-5" target="eCXqwsX-LsBgD2zF08TC-31">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-5" value="&lt;div&gt;read session info,&lt;br&gt;and start dst sub process&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="740" y="260" width="210" height="100" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-27" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="eCXqwsX-LsBgD2zF08TC-9" target="eCXqwsX-LsBgD2zF08TC-25">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-9" value="main process, methodPortSrc:&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;create session info from methodArgs like dst ip:port to connect to, and also uuid&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;startSub Process for src&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="180" y="260" width="210" height="100" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-10" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" edge="1" parent="1" source="eCXqwsX-LsBgD2zF08TC-39" target="eCXqwsX-LsBgD2zF08TC-9">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="285" y="140" as="sourcePoint" />
<mxPoint x="490" y="200" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-11" value="&lt;div&gt;read messages from destination,&lt;/div&gt;&lt;div&gt;extract the data field, and put&lt;/div&gt;&lt;div&gt;it on the tcp connection to&amp;nbsp;&lt;/div&gt;&lt;div&gt;source&lt;/div&gt;&lt;br&gt;read tcp stream from source&lt;div&gt;put them in a message and send&lt;/div&gt;&lt;div&gt;to destination&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="180" y="590" width="210" height="210" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-15" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="eCXqwsX-LsBgD2zF08TC-9" target="eCXqwsX-LsBgD2zF08TC-5">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="510" y="360" as="sourcePoint" />
<mxPoint x="560" y="310" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-16" value="send session info to dst node&lt;br&gt;to create a sub process that&lt;br&gt;will connect to the tcp endpoint" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="eCXqwsX-LsBgD2zF08TC-15">
<mxGeometry x="-0.0114" y="-3" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-17" value="read tcp stream from destination&lt;div&gt;put them in a message and send&lt;/div&gt;&lt;div&gt;to source&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;read messages from source,&lt;/div&gt;&lt;div&gt;extract the data field, and put&lt;/div&gt;&lt;div&gt;it on the tcp connection to&amp;nbsp;&lt;/div&gt;&lt;div&gt;destination&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="740" y="590" width="210" height="210" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-21" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1065" y="669.9999999999998" as="sourcePoint" />
<mxPoint x="1065" y="669.9999999999998" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-23" value="send data to source port" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="740" y="639.9999999999998" as="sourcePoint" />
<mxPoint x="390" y="639.9999999999998" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-29" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="eCXqwsX-LsBgD2zF08TC-24" target="eCXqwsX-LsBgD2zF08TC-11">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-24" value="start local tcp listener&lt;div&gt;using dst port&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="225" y="500" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-28" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="eCXqwsX-LsBgD2zF08TC-25" target="eCXqwsX-LsBgD2zF08TC-24">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-25" value="Start sub src process&lt;br&gt;sub process, methodPortSrc+UUID:&lt;div&gt;&lt;br&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="225" y="400" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-34" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="eCXqwsX-LsBgD2zF08TC-30" target="eCXqwsX-LsBgD2zF08TC-17">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-30" value="tcp service endpoint&lt;br&gt;ip:port" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="785" y="500" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-33" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="eCXqwsX-LsBgD2zF08TC-31" target="eCXqwsX-LsBgD2zF08TC-30">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-31" value="&lt;span style=&quot;font-size: 11px; text-wrap: nowrap; background-color: rgb(24, 20, 29);&quot;&gt;Start sub dst process&lt;/span&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="785" y="400" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-39" value="MESSAGE:&lt;div&gt;toNode: node1&lt;/div&gt;&lt;div&gt;method: portsrc&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;methodArgs:&lt;/div&gt;&lt;div&gt;- dstNode: node2&lt;/div&gt;&lt;div&gt;-&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="212.5" y="60" width="145" height="110" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-41" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="390" y="739.9999999999998" as="sourcePoint" />
<mxPoint x="740" y="739.9999999999998" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-42" value="send data to destination port" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="eCXqwsX-LsBgD2zF08TC-41">
<mxGeometry x="-0.3499" relative="1" as="geometry">
<mxPoint x="56" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-45" value="&lt;h1 style=&quot;margin-top: 0px;&quot;&gt;Sending Data&lt;/h1&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Add a structure to use in the data field of the message with 2 fields.&lt;/div&gt;&lt;div&gt;1. The actual data in bytes&lt;/div&gt;&lt;div&gt;2. Status messages like&amp;nbsp; &amp;nbsp; &amp;nbsp; disconnect, done, etc...&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;With sub processes on both sides we check the status messages to understand what to do with the connection.&lt;/div&gt;" style="text;html=1;whiteSpace=wrap;overflow=hidden;rounded=0;" vertex="1" parent="1">
<mxGeometry x="495" y="400" width="180" height="217" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-47" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="eCXqwsX-LsBgD2zF08TC-11" target="eCXqwsX-LsBgD2zF08TC-45">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="400" y="560" as="sourcePoint" />
<mxPoint x="450" y="510" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-49" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="eCXqwsX-LsBgD2zF08TC-17" target="eCXqwsX-LsBgD2zF08TC-45">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="710" y="590" as="sourcePoint" />
<mxPoint x="760" y="540" as="targetPoint" />
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
<diagram id="_iMASUMRkJR4dl4_wDkr" name="port example">
<mxGraphModel dx="2418" dy="928" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="k5I41uUsAggWzrHWQhdS-15" value="" style="shape=mxgraph.cisco.computers_and_peripherals.pc;html=1;pointerEvents=1;dashed=0;fillColor=#036897;strokeColor=#ffffff;strokeWidth=2;verticalLabelPosition=bottom;verticalAlign=top;align=center;outlineConnect=0;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="860" y="490.5" width="220" height="200" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-4" value="" style="shape=mxgraph.cisco.computers_and_peripherals.pc;html=1;pointerEvents=1;dashed=0;fillColor=#036897;strokeColor=#ffffff;strokeWidth=2;verticalLabelPosition=bottom;verticalAlign=top;align=center;outlineConnect=0;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="50" y="490.5" width="220" height="200" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-1" value="ctrl&lt;div style=&quot;font-size: 20px;&quot;&gt;node1&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="180" y="220" width="120" height="360" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-3" value="ctrl&lt;div style=&quot;font-size: 20px;&quot;&gt;node2&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="870" y="220" width="120" height="360" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-14" value="Start TCP Listener&amp;nbsp;&lt;div style=&quot;font-size: 20px;&quot;&gt;localhost:10022&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="134" y="719.5" width="132" height="30" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-16" value="Connect TCP&lt;div style=&quot;font-size: 20px;&quot;&gt;192.168.1.99:22&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="875" y="719.5" width="110" height="30" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-17" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;exitX=1.034;exitY=0.826;exitDx=0;exitDy=0;exitPerimeter=0;" edge="1" parent="1" source="k5I41uUsAggWzrHWQhdS-1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="520" y="700" as="sourcePoint" />
<mxPoint x="555.0000000000002" y="520.2901399067289" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-18" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="675.5000000000002" y="520.2949962658702" as="sourcePoint" />
<mxPoint x="870" y="520" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-20" value="" style="html=1;verticalLabelPosition=bottom;align=center;labelBackgroundColor=#ffffff;verticalAlign=top;strokeWidth=2;strokeColor=#0080F0;shadow=0;dashed=0;shape=mxgraph.ios7.icons.envelope_(message);" vertex="1" parent="1">
<mxGeometry x="400" y="470" width="60" height="70" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-21" value="" style="html=1;verticalLabelPosition=bottom;align=center;labelBackgroundColor=#ffffff;verticalAlign=top;strokeWidth=2;strokeColor=#0080F0;shadow=0;dashed=0;shape=mxgraph.ios7.icons.envelope_(message);" vertex="1" parent="1">
<mxGeometry x="750" y="470" width="60" height="70" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-22" value="TCP packet encapsulated in NATS message" style="shape=callout;whiteSpace=wrap;html=1;perimeter=calloutPerimeter;position2=0.42;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="370" y="320" width="120" height="130" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-23" value="TCP packet encapsulated in NATS message" style="shape=callout;whiteSpace=wrap;html=1;perimeter=calloutPerimeter;position2=0.42;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="720" y="320" width="120" height="130" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-24" value="NATS&lt;div&gt;Server&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontSize=21;" vertex="1" parent="1">
<mxGeometry x="555" y="220" width="120" height="490" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-25" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;exitX=1.021;exitY=0.172;exitDx=0;exitDy=0;exitPerimeter=0;" edge="1" parent="1" source="k5I41uUsAggWzrHWQhdS-1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="320" y="280" as="sourcePoint" />
<mxPoint x="866" y="282" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-26" value="" style="html=1;verticalLabelPosition=bottom;align=center;labelBackgroundColor=#ffffff;verticalAlign=top;strokeWidth=2;strokeColor=#0080F0;shadow=0;dashed=0;shape=mxgraph.ios7.icons.envelope_(message);" vertex="1" parent="1">
<mxGeometry x="400" y="240" width="60" height="70" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-27" value="Initial message to&lt;div style=&quot;font-size: 20px;&quot;&gt;connect a tcp stream from node1 on tcp port 10022&lt;br style=&quot;font-size: 20px;&quot;&gt;to&lt;br style=&quot;font-size: 20px;&quot;&gt;node2 tcp 192.168.1.99:22&lt;br&gt;and allow tcp forwarding&lt;/div&gt;" style="shape=callout;whiteSpace=wrap;html=1;perimeter=calloutPerimeter;position2=0.42;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="320" y="30" width="250" height="180" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-28" value="1" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=40;" vertex="1" parent="1">
<mxGeometry x="230" y="270" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-29" value="2" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=40;" vertex="1" parent="1">
<mxGeometry x="230" y="500" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-32" value="" style="verticalLabelPosition=bottom;verticalAlign=top;html=1;shape=mxgraph.basic.diag_round_rect;dx=6;whiteSpace=wrap;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="-70" y="581" width="150" height="80" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-33" value="SSH Client&lt;div style=&quot;font-size: 20px;&quot;&gt;connect to&lt;/div&gt;&lt;div style=&quot;font-size: 20px;&quot;&gt;localhost:10022&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="-55.5" y="606" width="125" height="30" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-34" value="" style="verticalLabelPosition=bottom;verticalAlign=top;html=1;shape=mxgraph.basic.diag_round_rect;dx=6;whiteSpace=wrap;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="1040" y="560" width="120" height="95" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-35" value="SSH Server&lt;div style=&quot;font-size: 20px;&quot;&gt;at&lt;/div&gt;&lt;div style=&quot;font-size: 20px;&quot;&gt;192.168.1:99&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="1034.5" y="562" width="125" height="110" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-37" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fillColor=#60a917;strokeColor=#2D7600;" edge="1" parent="1" target="k5I41uUsAggWzrHWQhdS-35">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="80" y="625" as="sourcePoint" />
<mxPoint x="520" y="640" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-38" value="&lt;font style=&quot;font-size: 30px;&quot;&gt;SSH Connection&lt;/font&gt;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="k5I41uUsAggWzrHWQhdS-37">
<mxGeometry x="-0.1926" y="1" relative="1" as="geometry">
<mxPoint x="-43" y="-4" as="offset" />
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

301
doc/drawings.drawio Normal file
View file

@ -0,0 +1,301 @@
<mxfile host="Electron" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/24.7.17 Chrome/128.0.6613.36 Electron/32.0.1 Safari/537.36" version="24.7.17" pages="4">
<diagram id="v0fDrbftQkuLOIMYALoA" name="key distribution flow">
<mxGraphModel dx="812" dy="603" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="tMiANJ_N9iX26Bk4R87N-14" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="tMiANJ_N9iX26Bk4R87N-1" target="tMiANJ_N9iX26Bk4R87N-4">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-1" value="node sends public key to central" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="80" y="160" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-16" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="tMiANJ_N9iX26Bk4R87N-2" target="tMiANJ_N9iX26Bk4R87N-11">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-2" value="ask if allowed to be put in central db" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="80" y="344" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-15" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="tMiANJ_N9iX26Bk4R87N-4" target="tMiANJ_N9iX26Bk4R87N-2">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-4" value="store key in not-acked db on central" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="80" y="250" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-18" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="tMiANJ_N9iX26Bk4R87N-5" target="tMiANJ_N9iX26Bk4R87N-12">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-5" value="create hash of all keys in db" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="80" y="520" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-17" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="tMiANJ_N9iX26Bk4R87N-11" target="tMiANJ_N9iX26Bk4R87N-5">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-11" value="operator allows the key onto central db" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="80" y="430" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-12" value="push the db with the hash to all nodes&lt;br&gt;&lt;br&gt;NB: should it be pushed to both ack&#39;ed and not ack&#39;ed nodes ?" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="80" y="620" width="120" height="130" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-21" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="tMiANJ_N9iX26Bk4R87N-20" target="tMiANJ_N9iX26Bk4R87N-1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-20" value="new key&lt;div&gt;detected in hello message&lt;/div&gt;" style="ellipse;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="80" y="50" width="120" height="80" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-31" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="tMiANJ_N9iX26Bk4R87N-22" target="tMiANJ_N9iX26Bk4R87N-23">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-22" value="keys update&lt;div&gt;receive&lt;/div&gt;&lt;div&gt;(node)&lt;/div&gt;" style="ellipse;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="320" y="50" width="120" height="80" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-25" value="yes" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="tMiANJ_N9iX26Bk4R87N-23" target="tMiANJ_N9iX26Bk4R87N-24">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-27" value="no" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="tMiANJ_N9iX26Bk4R87N-23" target="tMiANJ_N9iX26Bk4R87N-26">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-23" value="if length == 0" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="320" y="160" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-24" value="delete all keys" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="490" y="160" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-29" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="tMiANJ_N9iX26Bk4R87N-26" target="tMiANJ_N9iX26Bk4R87N-28">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-26" value="update and replace all keys" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="320" y="270" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-28" value="save to local file" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="320" y="360" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-30" value="keys allow&lt;div&gt;(central)&lt;/div&gt;" style="ellipse;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="650" y="50" width="120" height="80" as="geometry" />
</mxCell>
<mxCell id="tMiANJ_N9iX26Bk4R87N-32" value="get node name from operator to allow" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="650" y="160" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="WOR5zTzofjd4Fm8wHdib-1" value="move the node from the not-acked map to the ack&#39;ed map" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="650" y="270" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="WOR5zTzofjd4Fm8wHdib-2" value="create a new hash of all the nodes with keys in map" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="650" y="360" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="WOR5zTzofjd4Fm8wHdib-3" value="push updated map to all nodes" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="650" y="460" width="120" height="60" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
<diagram id="SR1WNxjGEkNWK4Vj7WTd" name="Page-3">
<mxGraphModel dx="812" dy="603" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
</root>
</mxGraphModel>
</diagram>
<diagram id="Miw9yFcv0oz29-3Sloob" name="portSrc-Dst">
<mxGraphModel dx="1160" dy="861" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="eCXqwsX-LsBgD2zF08TC-32" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="eCXqwsX-LsBgD2zF08TC-5" target="eCXqwsX-LsBgD2zF08TC-31">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-5" value="&lt;div&gt;read session info,&lt;br&gt;and start dst sub process&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="740" y="260" width="210" height="100" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-27" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="eCXqwsX-LsBgD2zF08TC-9" target="eCXqwsX-LsBgD2zF08TC-25">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-9" value="main process, methodPortSrc:&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;create session info from methodArgs like dst ip:port to connect to, and also uuid&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;startSub Process for src&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="180" y="260" width="210" height="100" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-10" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" edge="1" parent="1" source="eCXqwsX-LsBgD2zF08TC-39" target="eCXqwsX-LsBgD2zF08TC-9">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="285" y="140" as="sourcePoint" />
<mxPoint x="490" y="200" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-11" value="&lt;div&gt;read messages from destination,&lt;/div&gt;&lt;div&gt;extract the data field, and put&lt;/div&gt;&lt;div&gt;it on the tcp connection to&amp;nbsp;&lt;/div&gt;&lt;div&gt;source&lt;/div&gt;&lt;br&gt;read tcp stream from source&lt;div&gt;put them in a message and send&lt;/div&gt;&lt;div&gt;to destination&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="180" y="590" width="210" height="210" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-15" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="eCXqwsX-LsBgD2zF08TC-9" target="eCXqwsX-LsBgD2zF08TC-5">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="510" y="360" as="sourcePoint" />
<mxPoint x="560" y="310" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-16" value="send session info to dst node&lt;br&gt;to create a sub process that&lt;br&gt;will connect to the tcp endpoint" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="eCXqwsX-LsBgD2zF08TC-15">
<mxGeometry x="-0.0114" y="-3" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-17" value="read tcp stream from destination&lt;div&gt;put them in a message and send&lt;/div&gt;&lt;div&gt;to source&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;read messages from source,&lt;/div&gt;&lt;div&gt;extract the data field, and put&lt;/div&gt;&lt;div&gt;it on the tcp connection to&amp;nbsp;&lt;/div&gt;&lt;div&gt;destination&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="740" y="590" width="210" height="210" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-21" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1065" y="669.9999999999998" as="sourcePoint" />
<mxPoint x="1065" y="669.9999999999998" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-23" value="send data to source port" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="740" y="639.9999999999998" as="sourcePoint" />
<mxPoint x="390" y="639.9999999999998" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-29" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="eCXqwsX-LsBgD2zF08TC-24" target="eCXqwsX-LsBgD2zF08TC-11">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-24" value="start local tcp listener&lt;div&gt;using dst port&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="225" y="500" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-28" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="eCXqwsX-LsBgD2zF08TC-25" target="eCXqwsX-LsBgD2zF08TC-24">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-25" value="Start sub src process&lt;br&gt;sub process, methodPortSrc+UUID:&lt;div&gt;&lt;br&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="225" y="400" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-34" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="eCXqwsX-LsBgD2zF08TC-30" target="eCXqwsX-LsBgD2zF08TC-17">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-30" value="tcp service endpoint&lt;br&gt;ip:port" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="785" y="500" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-33" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="eCXqwsX-LsBgD2zF08TC-31" target="eCXqwsX-LsBgD2zF08TC-30">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-31" value="&lt;span style=&quot;font-size: 11px; text-wrap: nowrap; background-color: rgb(24, 20, 29);&quot;&gt;Start sub dst process&lt;/span&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="785" y="400" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-39" value="MESSAGE:&lt;div&gt;toNode: node1&lt;/div&gt;&lt;div&gt;method: portsrc&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;methodArgs:&lt;/div&gt;&lt;div&gt;- dstNode: node2&lt;/div&gt;&lt;div&gt;-&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="212.5" y="60" width="145" height="110" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-41" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="390" y="739.9999999999998" as="sourcePoint" />
<mxPoint x="740" y="739.9999999999998" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-42" value="send data to destination port" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="eCXqwsX-LsBgD2zF08TC-41">
<mxGeometry x="-0.3499" relative="1" as="geometry">
<mxPoint x="56" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-45" value="&lt;h1 style=&quot;margin-top: 0px;&quot;&gt;Sending Data&lt;/h1&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Add a structure to use in the data field of the message with 2 fields.&lt;/div&gt;&lt;div&gt;1. The actual data in bytes&lt;/div&gt;&lt;div&gt;2. Status messages like&amp;nbsp; &amp;nbsp; &amp;nbsp; disconnect, done, etc...&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;With sub processes on both sides we check the status messages to understand what to do with the connection.&lt;/div&gt;" style="text;html=1;whiteSpace=wrap;overflow=hidden;rounded=0;" vertex="1" parent="1">
<mxGeometry x="495" y="400" width="180" height="217" as="geometry" />
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-47" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="eCXqwsX-LsBgD2zF08TC-11" target="eCXqwsX-LsBgD2zF08TC-45">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="400" y="560" as="sourcePoint" />
<mxPoint x="450" y="510" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="eCXqwsX-LsBgD2zF08TC-49" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=2;rounded=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="eCXqwsX-LsBgD2zF08TC-17" target="eCXqwsX-LsBgD2zF08TC-45">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="710" y="590" as="sourcePoint" />
<mxPoint x="760" y="540" as="targetPoint" />
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
<diagram id="_iMASUMRkJR4dl4_wDkr" name="port example">
<mxGraphModel dx="2418" dy="928" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="k5I41uUsAggWzrHWQhdS-15" value="" style="shape=mxgraph.cisco.computers_and_peripherals.pc;html=1;pointerEvents=1;dashed=0;fillColor=#036897;strokeColor=#ffffff;strokeWidth=2;verticalLabelPosition=bottom;verticalAlign=top;align=center;outlineConnect=0;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="860" y="490.5" width="220" height="200" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-4" value="" style="shape=mxgraph.cisco.computers_and_peripherals.pc;html=1;pointerEvents=1;dashed=0;fillColor=#036897;strokeColor=#ffffff;strokeWidth=2;verticalLabelPosition=bottom;verticalAlign=top;align=center;outlineConnect=0;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="50" y="490.5" width="220" height="200" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-1" value="ctrl&lt;div style=&quot;font-size: 20px;&quot;&gt;node1&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="180" y="220" width="120" height="360" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-3" value="ctrl&lt;div style=&quot;font-size: 20px;&quot;&gt;node2&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="870" y="220" width="120" height="360" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-14" value="Start TCP Listener&amp;nbsp;&lt;div style=&quot;font-size: 20px;&quot;&gt;localhost:10022&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="134" y="719.5" width="132" height="30" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-16" value="Connect TCP&lt;div style=&quot;font-size: 20px;&quot;&gt;192.168.1.99:22&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="875" y="719.5" width="110" height="30" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-17" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;exitX=1.034;exitY=0.826;exitDx=0;exitDy=0;exitPerimeter=0;" edge="1" parent="1" source="k5I41uUsAggWzrHWQhdS-1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="520" y="700" as="sourcePoint" />
<mxPoint x="555.0000000000002" y="520.2901399067289" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-18" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="675.5000000000002" y="520.2949962658702" as="sourcePoint" />
<mxPoint x="870" y="520" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-20" value="" style="html=1;verticalLabelPosition=bottom;align=center;labelBackgroundColor=#ffffff;verticalAlign=top;strokeWidth=2;strokeColor=#0080F0;shadow=0;dashed=0;shape=mxgraph.ios7.icons.envelope_(message);" vertex="1" parent="1">
<mxGeometry x="400" y="470" width="60" height="70" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-21" value="" style="html=1;verticalLabelPosition=bottom;align=center;labelBackgroundColor=#ffffff;verticalAlign=top;strokeWidth=2;strokeColor=#0080F0;shadow=0;dashed=0;shape=mxgraph.ios7.icons.envelope_(message);" vertex="1" parent="1">
<mxGeometry x="750" y="470" width="60" height="70" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-22" value="TCP packet encapsulated in NATS message" style="shape=callout;whiteSpace=wrap;html=1;perimeter=calloutPerimeter;position2=0.42;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="370" y="320" width="120" height="130" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-23" value="TCP packet encapsulated in NATS message" style="shape=callout;whiteSpace=wrap;html=1;perimeter=calloutPerimeter;position2=0.42;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="720" y="320" width="120" height="130" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-24" value="NATS&lt;div&gt;Server&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;fontSize=21;" vertex="1" parent="1">
<mxGeometry x="555" y="220" width="120" height="490" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-25" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;exitX=1.021;exitY=0.172;exitDx=0;exitDy=0;exitPerimeter=0;" edge="1" parent="1" source="k5I41uUsAggWzrHWQhdS-1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="320" y="280" as="sourcePoint" />
<mxPoint x="866" y="282" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-26" value="" style="html=1;verticalLabelPosition=bottom;align=center;labelBackgroundColor=#ffffff;verticalAlign=top;strokeWidth=2;strokeColor=#0080F0;shadow=0;dashed=0;shape=mxgraph.ios7.icons.envelope_(message);" vertex="1" parent="1">
<mxGeometry x="400" y="240" width="60" height="70" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-27" value="Initial message to&lt;div style=&quot;font-size: 20px;&quot;&gt;connect a tcp stream from node1 on tcp port 10022&lt;br style=&quot;font-size: 20px;&quot;&gt;to&lt;br style=&quot;font-size: 20px;&quot;&gt;node2 tcp 192.168.1.99:22&lt;br&gt;and allow tcp forwarding&lt;/div&gt;" style="shape=callout;whiteSpace=wrap;html=1;perimeter=calloutPerimeter;position2=0.42;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="320" y="30" width="250" height="180" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-28" value="1" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=40;" vertex="1" parent="1">
<mxGeometry x="230" y="270" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-29" value="2" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=40;" vertex="1" parent="1">
<mxGeometry x="230" y="500" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-32" value="" style="verticalLabelPosition=bottom;verticalAlign=top;html=1;shape=mxgraph.basic.diag_round_rect;dx=6;whiteSpace=wrap;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="-70" y="581" width="150" height="80" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-33" value="SSH Client&lt;div style=&quot;font-size: 20px;&quot;&gt;connect to&lt;/div&gt;&lt;div style=&quot;font-size: 20px;&quot;&gt;localhost:10022&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="-55.5" y="606" width="125" height="30" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-34" value="" style="verticalLabelPosition=bottom;verticalAlign=top;html=1;shape=mxgraph.basic.diag_round_rect;dx=6;whiteSpace=wrap;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="1040" y="560" width="120" height="95" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-35" value="SSH Server&lt;div style=&quot;font-size: 20px;&quot;&gt;at&lt;/div&gt;&lt;div style=&quot;font-size: 20px;&quot;&gt;192.168.1:99&lt;/div&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=20;" vertex="1" parent="1">
<mxGeometry x="1034.5" y="562" width="125" height="110" as="geometry" />
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-37" value="" style="shape=flexArrow;endArrow=classic;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fillColor=#60a917;strokeColor=#2D7600;" edge="1" parent="1" target="k5I41uUsAggWzrHWQhdS-35">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="80" y="625" as="sourcePoint" />
<mxPoint x="520" y="640" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="k5I41uUsAggWzrHWQhdS-38" value="&lt;font style=&quot;font-size: 30px;&quot;&gt;SSH Connection&lt;/font&gt;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="k5I41uUsAggWzrHWQhdS-37">
<mxGeometry x="-0.1926" y="1" relative="1" as="geometry">
<mxPoint x="-43" y="-4" as="offset" />
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

4
doc/portforward-ssh.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 497 KiB

View file

@ -0,0 +1,335 @@
// request_dummy.go
//
// request_dummy.go are put here as an example for how a process via it's
// method can spawn new sub processes. The only purpose of the 'main' method
// is to to prepare and start one or more sub processes that will handle the
// actual logic.
//
// Sub processes can be either short lived to do some work, like copying a
// file, or long lived like a server that listens for incoming connections
// to serve a web page.
package ctrl
import (
"context"
"fmt"
"log/slog"
"strconv"
"time"
"github.com/fxamacker/cbor/v2"
"github.com/google/uuid"
)
type dummyInitialData struct {
UUID string
SourceNode Node
SourceSubMethod Method
SourcePort string
DestinationNode Node
DestinationSubMethod Method
DestinationPort string
MaxSessionTimeout int
}
// The method that will handle the initial message, and setup whats needed for an outbound connection.
func methodDummySrc(proc process, message Message, node string) ([]byte, error) {
go func() {
defer proc.processes.wg.Done()
// Message example to start an outbound connection
// ---
// - toNode:
// - node1
// method: dummySrc
// methodArgs:
// - node1 # destination node
// - 8080 # destination port
// - 100 # max session timeout
// replymethod: console
const (
arg0_DestinationNode = 0
arg1_DestinationPort = 1
arg2_MaxSessionTimeout = 2
)
wantMethodArgs := "want: (0)destination-node, (1)destination-port, (2)max-session-timeout"
pid := dummyInitialData{
UUID: uuid.New().String(),
SourceNode: proc.node,
}
proc.processes.wg.Add(1)
if len(message.MethodArgs) < 2 {
slog.Error("methodDummySrc: got <2 number methodArgs", "want", wantMethodArgs)
return
}
// Destination node
if message.MethodArgs[arg0_DestinationNode] == "" {
slog.Error("methodDummySrc: no destination node specified in method args", "want", wantMethodArgs)
return
}
pid.DestinationNode = Node(message.MethodArgs[arg0_DestinationNode])
// Destination port
if message.MethodArgs[arg1_DestinationPort] == "" {
slog.Error("methodDummySrc: no destination port specified in method args", "want", wantMethodArgs)
return
}
pid.DestinationPort = message.MethodArgs[arg1_DestinationPort]
// Max session timeout
if message.MethodArgs[arg2_MaxSessionTimeout] == "" {
slog.Error("methodDummySrc: no max session time specified in method args", "want", wantMethodArgs)
return
}
n, err := strconv.Atoi(message.MethodArgs[arg2_MaxSessionTimeout])
if err != nil {
slog.Error("methodDummySrc: unable to convert max session timeout from string to int", "error", err)
return
}
pid.MaxSessionTimeout = n
// Create a child context to use with the procFunc with timeout set to the max allowed total copy time
// specified in the message.
var ctx context.Context
var cancel context.CancelFunc
func() {
ctx, cancel = context.WithTimeout(proc.ctx, time.Second*time.Duration(pid.MaxSessionTimeout))
}()
srcSubProcessName := fmt.Sprintf("%v.%v", SUBDummySrc, pid.UUID)
pid.SourceSubMethod = Method(srcSubProcessName)
// Create a new subprocess that will handle the network source side.
subject := newSubjectNoVerifyHandler(pid.SourceSubMethod, node)
dummySrcSubProc := newSubProcess(ctx, proc.server, subject)
// Attach a procfunc to the sub process that will do the actual logic with the network source port.
//
// TODO: We need to initiate the network connection here to
// get hold of the source port, and send that over to the
// destination node.
// NB: assign pid.SourcePort a value.
dummySrcSubProc.procFunc = dummySrcSubProcFunc(pid, message, cancel)
// Assign a handler to the sub process for receiving messages for the subprocess.
dummySrcSubProc.handler = dummySrcSubHandler()
// Start sub process. The process will be killed when the context expires.
go dummySrcSubProc.start()
// Prepare the naming for the dst method here so we can have all the
// information in the pid from the beginning at both ends and not have
// to generate naming on the destination node.
dstSubProcessName := fmt.Sprintf("%v.%v", SUBDummyDst, pid.UUID)
pid.DestinationSubMethod = Method(dstSubProcessName)
fmt.Printf("DEBUG: methodDummySrc, pid: %+v\n", pid)
// Marshal the data payload to send to the dst.
cb, err := cbor.Marshal(pid)
if err != nil {
er := fmt.Errorf("error: methodDummySrc: cbor marshalling failed: %v, message: %v", err, message)
proc.errorKernel.errSend(proc, message, er, logWarning)
cancel()
}
// Send a message to the the destination node, to also start up a sub
// process there.
msg := Message{
ToNode: pid.DestinationNode,
Method: DummyDst,
Data: cb,
ACKTimeout: message.ACKTimeout,
Retries: message.Retries,
ReplyMethod: Console, // TODO: Adding for debug output
ReplyACKTimeout: message.ReplyACKTimeout,
ReplyRetries: message.ReplyRetries,
}
proc.newMessagesCh <- msg
replyData := fmt.Sprintf("info: succesfully started dummy source process: procName=%v, srcNode=%v, sourcePort=%v, dstNode=%v, starting sub process=%v", dummySrcSubProc.processName, node, pid.SourcePort, pid.DestinationNode, srcSubProcessName)
newReplyMessage(proc, message, []byte(replyData))
}()
ackMsg := []byte("confirmed from: " + node + ": " + fmt.Sprint(message.ID))
return ackMsg, nil
}
func methodDummyDst(proc process, message Message, node string) ([]byte, error) {
var subProcessName string
proc.processes.wg.Add(1)
go func() {
defer proc.processes.wg.Done()
// Get the status message sent from source.
var pid dummyInitialData
err := cbor.Unmarshal(message.Data, &pid)
if err != nil {
er := fmt.Errorf("error: methodDummyDst: failed to cbor Unmarshal data: %v, message=%v", err, message)
proc.errorKernel.errSend(proc, message, er, logWarning)
return
}
fmt.Printf(" *** DEBUG: MaxSessionTimeout: %v\n", pid.MaxSessionTimeout)
// Create a child context to use with the procFunc
var ctx context.Context
var cancel context.CancelFunc
func() {
ctx, cancel = context.WithTimeout(proc.ctx, time.Second*time.Duration(pid.MaxSessionTimeout))
}()
// Start preparing to start a sub process.
sub := newSubjectNoVerifyHandler(pid.DestinationSubMethod, node)
// But first...
// Check if we already got a sub process registered and started with
// the processName. If true, return here and don't start up another
// process.
//
// This check is put in here if a message for some reason are
// received more than once. The reason that this might happen can be
// that a message for the same copy request was received earlier, but
// was unable to start up within the timeout specified. The Sender of
// that request will then resend the message, but at the time that
// second message is received the subscriber process started for the
// previous message is then fully up and running, so we just discard
// that second message in those cases.
pn := processNameGet(sub.name())
// fmt.Printf("\n\n *** DEBUG: processNameGet: %v\n\n", pn)
proc.processes.active.mu.Lock()
_, ok := proc.processes.active.procNames[pn]
proc.processes.active.mu.Unlock()
if ok {
proc.errorKernel.logDebug("methodCopyDst: subprocesses already existed, will not start another subscriber for", "processName", pn)
// If the process name already existed we return here before any
// new information is registered in the process map and we avoid
// having to clean that up later.
return
}
// Create a new sub process.
dummyDstSubProc := newSubProcess(ctx, proc.server, sub)
// Give the sub process a procFunc that will do the actual networking within a procFunc,
dummyDstSubProc.procFunc = dummyDstSubProcFunc(pid, message, cancel)
// assign a handler to the sub process
dummyDstSubProc.handler = dummyDstSubHandler()
// The process will be killed when the context expires.
go dummyDstSubProc.start()
replyData := fmt.Sprintf("info: succesfully initiated dummy destination process: procName=%v, srcNode=%v, starting sub process=%v for the actual copying", dummyDstSubProc.processName, node, subProcessName)
newReplyMessage(proc, message, []byte(replyData))
}()
ackMsg := []byte("confirmed from: " + node + ": " + fmt.Sprint(message.ID))
return ackMsg, nil
}
func dummySrcSubHandler() func(process, Message, string) ([]byte, error) {
h := func(proc process, message Message, node string) ([]byte, error) {
select {
case <-proc.ctx.Done():
proc.errorKernel.logDebug("copySrcHandler: process ended", "processName", proc.processName)
case proc.procFuncCh <- message:
proc.errorKernel.logDebug("copySrcHandler: passing message over to procFunc", "processName", proc.processName)
}
return nil, nil
}
return h
}
func dummyDstSubHandler() func(process, Message, string) ([]byte, error) {
h := func(proc process, message Message, node string) ([]byte, error) {
select {
case <-proc.ctx.Done():
proc.errorKernel.logDebug("copyDstHandler: process ended", "processName", proc.processName)
case proc.procFuncCh <- message:
proc.errorKernel.logDebug("copyDstHandler: passing message over to procFunc", "processName", proc.processName)
}
return nil, nil
}
return h
}
func dummySrcSubProcFunc(pid dummyInitialData, initialMessage Message, cancel context.CancelFunc) func(context.Context, process, chan Message) error {
pf := func(ctx context.Context, proc process, procFuncCh chan Message) error {
fmt.Printf("STARTED PROCFUNC: %v\n", proc.subject.name())
defer fmt.Println("dummySrcProcFunc: canceled procFunc", "processName", proc.processName)
for {
select {
case <-ctx.Done():
fmt.Println("dummySrcProcFunc: canceling procFunc", "processName", proc.processName)
return nil
// Pick up the message recived by the copySrcSubHandler.
case message := <-procFuncCh:
_ = message
for i := 0; i < 20; i++ {
fmt.Printf("dst: %v\n", i)
time.Sleep(time.Second * 1)
}
}
}
}
return pf
}
func dummyDstSubProcFunc(pid dummyInitialData, message Message, cancel context.CancelFunc) func(context.Context, process, chan Message) error {
pf := func(ctx context.Context, proc process, procFuncCh chan Message) error {
fmt.Printf("STARTED PROCFUNC: %v\n", proc.subject.name())
defer fmt.Println("dummyDstProcFunc: canceled procFunc", "processName", proc.processName)
for {
select {
case <-ctx.Done():
fmt.Println("dummyDstProcFunc: got <-ctx.Done() cancelling procFunc", "processName", proc.processName)
return nil
// Pick up the message recived by the copySrcSubHandler.
case message := <-procFuncCh:
_ = message
for i := 0; i < 20; i++ {
fmt.Printf("dst: %v\n", i)
time.Sleep(time.Second * 1)
}
}
}
}
return pf
}

View file

@ -40,3 +40,4 @@
- [ctrl as github action runner](usecase-ctrl-as-github-action-runner.md)
- [ctrl as prometheus collector](usecase-ctrl-as-prometheus-collector.md)
- [ctrl as tcp forwarder for ssh](usecase-ctrl-as-tcp-forwarder-for-ssh.md)

View file

@ -0,0 +1,44 @@
# ctrl as tcp forwarder for ssh
ctrl can be used to forward a TCP stream between two nodes. The Individual TCP packet will be read from a TCP listener on a node, put into a standard ctrl message, the message are then sent to the destination node using NATS PUB/SUB where the TCP packet is extracted, and written to the TCP connection.
In the follwing example we have two nodes, where we can think of **node1** as the local node running on your computer, and node2 are the remote node running on a server somewhere.
We want to connect with ssh to node2, but it is not directly available from the network where the local node1 resides, so we use ctrl as a proxy server and forward the ssh tcp stream.
</style>
</head>
<body>
<p align="center"><img src="https://github.com/postmannen/ctrl/blob/main/doc/usecase-portforward-ssh.svg?raw=true" /></p>
</body>
## Steps
1. Create the message with forwarding details, and copy iy into **node1's readfolder**.
```yaml
---
- toNodes:
- node1 # The source node where we start the tcp listener
method: portSrc # The ctrl tcp forward method
methodArgs:
- node2 # The destination node who connects to the actual endpoint
- 192.168.99.1:22 # The ip address and port we want to connect to from endpoint.
- :10022 # The local port to start the listener on node1
- 30 # How many seconds the tcp forwarder should be active
methodTimeout: 30 # Same as above, but at the method level. Set them to the same.
replyMethod: console # Do logging to console on node1
ACKTimeout: 0 # No ACK'in of messages. Fire and forget.
```
1. On **node1** a process with a TCP listener will be started at **0.0.0.0:10022**.
1. The process on **node1** will then send a message to **node2** to start up a process and connect to the endpoint defined in the **methodArgs**.
1. The forwarding are now up running, and we can use a ssh client to connect to the endpoint which are now forwarded the **node1** on port **10022**.
```bash
ssh -p 10022 user@localhost
```
The forwarding will automatically end after the timeperiod specified, which in this example is 30 seconds.

View file

@ -345,7 +345,7 @@ func (s *server) readFolder() {
fh, err := os.Open(event.Name)
if err != nil {
er := fmt.Errorf("error: readFolder: failed to open readFile from readFolder: %v", err)
s.errorKernel.errSend(s.processInitial, Message{}, er, logWarning)
s.errorKernel.errSend(s.processInitial, Message{}, er, logDebug)
return
}

View file

@ -180,6 +180,10 @@ func (p *processes) Start(proc process) {
}
proc.startup.startProcess(proc, PublicKey, nil)
// TODO: Create configuration flags for enabling processes.
proc.startup.startProcess(proc, PortSrc, nil)
proc.startup.startProcess(proc, PortDst, nil)
}
// Stop all subscriber processes.

View file

@ -104,6 +104,17 @@ const (
SUBCopySrc Method = "subCopySrc"
// Write the destination copied to some node.
SUBCopyDst Method = "subCopyDst"
PortSrc Method = "portSrc"
PortDst Method = "PortDst"
SUBPortSrc Method = "subPortSrc"
SUBPortDst Method = "subPortDst"
DummySrc Method = "dummySrc"
DummyDst Method = "dummyDst"
SUBDummySrc Method = "subDummySrc"
SUBDummyDst Method = "subDummyDst"
// Hello I'm here message.
Hello Method = "hello"
HelloPublisher Method = "helloPublisher"
@ -185,6 +196,10 @@ func (m Method) GetMethodsAvailable() MethodsAvailable {
CopyDst: Handler(methodCopyDst),
SUBCopySrc: Handler(methodSUB),
SUBCopyDst: Handler(methodSUB),
PortSrc: Handler(methodPortSrc),
PortDst: Handler(methodPortDst),
SUBPortSrc: Handler(methodSUB),
SUBPortDst: Handler(methodSUB),
Hello: Handler(methodHello),
// The hello publisher will not subscribe for messages, it will
// only start a procFunc, so we we don't need a handler with a method,
@ -258,7 +273,7 @@ func methodInitial(proc process, message Message, node string) ([]byte, error) {
// ----
// place holder method used for sub processes.
// Methods used in sub processes are defined within the the requests
// Methods used in sub processes are defined within the requests
// they are spawned in, so this type is primarily for us to use the
// same logic with sub process requests as we do with normal requests.
func methodSUB(proc process, message Message, node string) ([]byte, error) {

708
requests_port.go Normal file
View file

@ -0,0 +1,708 @@
package ctrl
import (
"context"
"fmt"
"io"
"log"
"log/slog"
"net"
"sort"
"strconv"
"strings"
"sync"
"time"
"github.com/fxamacker/cbor/v2"
"github.com/google/uuid"
)
// portInitialData is the data that is sent to the source and destination nodes when a port forwarding is created.
type portInitialData struct {
UUID string
SourceNode Node
SourceSubMethod Method
SourceIPAndPort string
DestinationNode Node
DestinationSubMethod Method
DestinationIPAndPort string
MaxSessionTimeout int
}
// NewPortInitialData creates a new portInitialData struct.
func NewPortInitialData(uuid string, sourceNode Node, sourceSubMethod Method, sourcePort string, destinationNode Node, destinationSubMethod Method, destinationPort string, maxSessionTimeout int) portInitialData {
pid := portInitialData{
UUID: uuid,
SourceNode: sourceNode,
SourceSubMethod: sourceSubMethod,
SourceIPAndPort: sourcePort,
DestinationNode: destinationNode,
DestinationSubMethod: destinationSubMethod,
DestinationIPAndPort: destinationPort,
MaxSessionTimeout: maxSessionTimeout,
}
return pid
}
// The main method that will handle the initial message, and setup whats needed for an outbound connection.
// It will setup a sub process that will handle the individual session of the port forwarding on the
// source node, and also setup the local tcp listener on the source node that a client can connect to.
//
// NB: All logic for the forwarding are done in the subprocesses started.
func methodPortSrc(proc process, message Message, node string) ([]byte, error) {
go func() {
defer proc.processes.wg.Done()
// Message example to start an outbound connection
// ---
// - toNode:
// - node1
// method: portSrc
// methodArgs:
// - node1 # destination node
// - localhost:8080 # destination node and port
// - localhost:8090 # source node and port
// - 100 # max session timeout
// replymethod: console
const (
arg0_DestinationNode = 0
arg1_DestinationIPAndPort = 1
arg2_SourceIPandPort = 2
arg3_MaxSessionTimeout = 3
)
wantMethodArgs := "want: (0)destination-node, (1)destination-ip-and-port, (2) source-ip-and-port, (3)max-session-timeout"
uuid := uuid.New().String()
sourceNode := proc.node
proc.processes.wg.Add(1)
if len(message.MethodArgs) < arg3_MaxSessionTimeout {
slog.Error("methodPortSrc: to few methodArgs defined in message", "want", wantMethodArgs)
return
}
// Destination node
if message.MethodArgs[arg0_DestinationNode] == "" {
slog.Error("methodPortSrc: no destination node specified in method args", "want", wantMethodArgs)
return
}
destinationNode := Node(message.MethodArgs[arg0_DestinationNode])
// Destination port
if message.MethodArgs[arg1_DestinationIPAndPort] == "" {
slog.Error("methodPortSrc: no destination port specified in method args", "want", wantMethodArgs)
return
}
destinationIPAndPort := message.MethodArgs[arg1_DestinationIPAndPort]
// Source port
if message.MethodArgs[arg2_SourceIPandPort] == "" {
slog.Error("methodPortSrc: no source port specified in method args", "want", wantMethodArgs)
return
}
sourceIPAndPort := message.MethodArgs[arg2_SourceIPandPort]
// Max session timeout
if message.MethodArgs[arg3_MaxSessionTimeout] == "" {
slog.Error("methodPortSrc: no max session time specified in method args", "want", wantMethodArgs)
return
}
n, err := strconv.Atoi(message.MethodArgs[arg3_MaxSessionTimeout])
if err != nil {
slog.Error("methodPortSrc: unable to convert max session timeout from string to int", "error", err)
return
}
maxSessionTimeout := n
// Prepare the naming for the dst method here so we can have all the
// information in the pid from the beginning at both ends and not have
// to generate naming on the destination node.
dstSubProcessName := fmt.Sprintf("%v.%v", SUBPortDst, uuid)
destinationSubMethod := Method(dstSubProcessName)
// ------- Create the source sub process -------
// Create a child context to use with the procFunc with timeout set to the max allowed total copy time
// specified in the message.
var ctx context.Context
var cancel context.CancelFunc
func() {
ctx, cancel = context.WithTimeout(proc.ctx, time.Second*time.Duration(maxSessionTimeout))
}()
srcSubProcessName := fmt.Sprintf("%v.%v", SUBPortSrc, uuid)
sourceSubMethod := Method(srcSubProcessName)
// Create a new subprocess that will handle the network source side.
subject := newSubjectNoVerifyHandler(sourceSubMethod, node)
portSrcSubProc := newSubProcess(ctx, proc.server, subject)
// Create a new portInitialData with all the information we need for starting the sub processes.
// NB: Using the destination port as the source port on the source process for now.
pid := NewPortInitialData(uuid, sourceNode, sourceSubMethod, sourceIPAndPort, destinationNode, destinationSubMethod, destinationIPAndPort, maxSessionTimeout)
// Attach a procfunc to the sub process that will do the actual logic with the network source port.
portSrcSubProc.procFunc = portSrcSubProcFunc(pid, message, cancel)
// Assign a handler to the sub process for receiving messages for the subprocess.
portSrcSubProc.handler = portSrcSubHandler()
// Start sub process. The process will be killed when the context expires.
go portSrcSubProc.start()
fmt.Printf("DEBUG: methodPortSrc, pid: %+v\n", pid)
// ------- Prepare the data payload to send to the dst to start the dst sub process -------
cb, err := cbor.Marshal(pid)
if err != nil {
er := fmt.Errorf("error: methodPortSrc: cbor marshalling failed: %v, message: %v", err, message)
proc.errorKernel.errSend(proc, message, er, logWarning)
cancel()
}
// Send a message to the the destination node, to also start up a sub
// process there.
msg := Message{
ToNode: pid.DestinationNode,
Method: PortDst,
Data: cb,
ACKTimeout: message.ACKTimeout,
Retries: message.Retries,
ReplyMethod: Console, // TODO: Adding for debug output
ReplyACKTimeout: message.ReplyACKTimeout,
ReplyRetries: message.ReplyRetries,
}
proc.newMessagesCh <- msg
replyData := fmt.Sprintf("info: succesfully started port source process: procName=%v, srcNode=%v, sourcePort=%v, dstNode=%v, starting sub process=%v", portSrcSubProc.processName, node, pid.SourceIPAndPort, pid.DestinationNode, srcSubProcessName)
newReplyMessage(proc, message, []byte(replyData))
}()
ackMsg := []byte("confirmed from: " + node + ": " + fmt.Sprint(message.ID))
return ackMsg, nil
}
// The main method that will handle the initial message sendt from the source process, and setup
// sub process that will handle the individual session of the port forwarding, and also
// setup the connection the final tcp endpoint on the destination node.
//
// NB: All logic for the forwarding are done in the subprocesses started.
func methodPortDst(proc process, message Message, node string) ([]byte, error) {
var subProcessName string
proc.processes.wg.Add(1)
go func() {
defer proc.processes.wg.Done()
// Get the status message sent from source.
var pid portInitialData
err := cbor.Unmarshal(message.Data, &pid)
if err != nil {
er := fmt.Errorf("error: methodPortDst: failed to cbor Unmarshal data: %v, message=%v", err, message)
proc.errorKernel.errSend(proc, message, er, logWarning)
return
}
fmt.Printf(" *** DEBUG: ON DST: got pid: %+v\n", pid)
// Create a child context to use with the procFunc
var ctx context.Context
var cancel context.CancelFunc
func() {
ctx, cancel = context.WithTimeout(proc.ctx, time.Second*time.Duration(pid.MaxSessionTimeout))
}()
// Start preparing to start a sub process.
sub := newSubjectNoVerifyHandler(pid.DestinationSubMethod, node)
// But first...
// Check if we already got a sub process registered and started with
// the processName. If true, return here and don't start up another
// process.
//
// This check is put in here if a message for some reason are
// received more than once. The reason that this might happen can be
// that a message for the same copy request was received earlier, but
// was unable to start up within the timeout specified. The Sender of
// that request will then resend the message, but at the time that
// second message is received the subscriber process started for the
// previous message is then fully up and running, so we just discard
// that second message in those cases.
pn := processNameGet(sub.name())
// fmt.Printf("\n\n *** DEBUG: processNameGet: %v\n\n", pn)
proc.processes.active.mu.Lock()
_, ok := proc.processes.active.procNames[pn]
proc.processes.active.mu.Unlock()
if ok {
proc.errorKernel.logDebug("methodCopyDst: subprocesses already existed, will not start another subscriber for", "processName", pn)
// If the process name already existed we return here before any
// new information is registered in the process map and we avoid
// having to clean that up later.
return
}
// Create a new sub process.
portDstSubProc := newSubProcess(ctx, proc.server, sub)
// Give the sub process a procFunc that will do the actual networking within a procFunc,
portDstSubProc.procFunc = portDstSubProcFunc(pid, message, cancel)
// assign a handler to the sub process
portDstSubProc.handler = portDstSubHandler()
// The process will be killed when the context expires.
go portDstSubProc.start()
replyData := fmt.Sprintf("info: succesfully initiated port destination process: procName=%v, srcNode=%v, starting sub process=%v for the actual copying", portDstSubProc.processName, node, subProcessName)
newReplyMessage(proc, message, []byte(replyData))
}()
ackMsg := []byte("confirmed from: " + node + ": " + fmt.Sprint(message.ID))
return ackMsg, nil
}
// portSrcSubHandler is the handler for messages received and destined for the port source sub process.
// It will pass the message to the procFunc of the port source sub process, which will do the actual
// handling of the messages.
func portSrcSubHandler() func(process, Message, string) ([]byte, error) {
h := func(proc process, message Message, node string) ([]byte, error) {
select {
case <-proc.ctx.Done():
proc.errorKernel.logDebug("copySrcHandler: process ended", "processName", proc.processName)
case proc.procFuncCh <- message:
proc.errorKernel.logDebug("copySrcHandler: passing message over to procFunc", "processName", proc.processName)
}
return nil, nil
}
return h
}
// portDstSubHandler is the handler for messages received and destined for the port destination sub process.
// It will pass the message to the procFunc of the port destination sub process, which will do the actual
// handling of the messages.
func portDstSubHandler() func(process, Message, string) ([]byte, error) {
h := func(proc process, message Message, node string) ([]byte, error) {
select {
case <-proc.ctx.Done():
proc.errorKernel.logDebug("copyDstHandler: process ended", "processName", proc.processName)
case proc.procFuncCh <- message:
proc.errorKernel.logDebug("copyDstHandler: passing message over to procFunc", "processName", proc.processName)
}
return nil, nil
}
return h
}
type portData struct {
OK bool
ErrorMsg string
Data []byte
ID int
}
// portSrcSubProcFunc is the function that will be run by the portSrcSubProc process.
// It will listen on the source IP and port, and send messages to the destination node
// to write the data to the destination IP and port.
// It will also handle the incomming messages from the destination node and write the
// data in the message to the source IP and port.
func portSrcSubProcFunc(pid portInitialData, initialMessage Message, cancel context.CancelFunc) func(context.Context, process, chan Message) error {
pf := func(ctx context.Context, proc process, procFuncCh chan Message) error {
fmt.Printf("STARTED PROCFUNC: %v\n", proc.subject.name())
defer cancel()
defer fmt.Println("portSrcProcFunc: canceled procFunc", "processName", proc.processName)
listener, err := net.Listen("tcp", pid.SourceIPAndPort)
if err != nil {
// TODO: Send a message to destination sub process that there was an error,
// and that it should stop.
fmt.Printf("error: portSrcSubProcFunc: net.Listen failed. err=%v\n", err)
return err
}
// Since the data to write to the src network listener is coming from the dst node in
// the form of a message, we need to write it to a channel that the listener can read from.
srcWriteCh := make(chan []byte, 1)
// Start a goroutine to handle the tcp listener.
go func() {
for {
conn, err := listener.Accept()
if err != nil {
// TODO: Send a message to destination sub process that there was an error,
// and that it should stop.
fmt.Printf("error: portSrcSubProcFunc: listener.Accept failed. err=%v\n", err)
return
}
defer func() {
conn.Close()
listener.Close()
fmt.Println(" DEBUG: Canceled portSrcSubProcFunc connection and listener")
}()
// Read the data from the tcp connection, create messages from it, and
// send it to the dst node.
go func() {
id := 0
for {
b := make([]byte, 65535)
n, err := conn.Read(b)
if err != nil {
log.Printf("error: portSrcSubProcFunc: conn.Read failed. err=%v", err)
return
}
pd := portData{
ID: id,
Data: b[:n],
}
id++
cb, err := cbor.Marshal(pd)
if err != nil {
log.Fatalf("error: portDstSubProcFunc: cbor marshalling failed. err=%v", err)
}
msg := Message{
ToNode: pid.DestinationNode,
Method: Method(pid.DestinationSubMethod),
Data: cb,
ACKTimeout: initialMessage.ACKTimeout,
Retries: initialMessage.Retries,
ReplyMethod: None,
}
fmt.Printf(" ******* SRC: Created message to send with id : %v\n", pd.ID)
select {
case <-ctx.Done():
fmt.Println("portSrcProcFunc: canceling procFunc", "processName", proc.processName)
return
case proc.newMessagesCh <- msg:
fmt.Printf(" ---->: Sending message, id: %v, size: %v\n", pd.ID, len(pd.Data))
}
}
}()
// We only allow 1 connection to the listener, so we're not starting a new goroutine for each
// connection.
for {
select {
case <-ctx.Done():
fmt.Println("portSrcProcFunc: canceling procFunc", "processName", proc.processName)
return
case b := <-srcWriteCh:
n, err := conn.Write(b)
if err != nil {
log.Printf("error: portSrcSubProcFunc: conn.Write failed. err=%v", err)
return
}
fmt.Printf("--------> conn: portSrcSubProcFunc: wrote %v bytes to connection\n", n)
}
}
}
}()
// -----------------------------------------------------------------------------------
// Read from messages from dst node and write to the source network connection.
// -----------------------------------------------------------------------------------
expectedID := -1
buffer := NewPortSortedBuffer()
for {
select {
case <-ctx.Done():
fmt.Println("portSrcProcFunc: canceling procFunc", "processName", proc.processName)
return nil
// Handle the messages reveived from the sub process on the src node.
// The messages will contain the data to be sent to the dst node.
case message := <-procFuncCh:
expectedID++
var pd portData
err := cbor.Unmarshal(message.Data, &pd)
if err != nil {
log.Fatalf("error: portSrcSubProcFunc: cbor unmarshalling failed. err=%v", err)
}
fmt.Printf("<---- GOT DATA ON SRC, pdd.OK:%v, length of pddData ::: %v\n", pd.OK, len(pd.Data))
buffer.Push(pd)
err = func() error {
nextID, _ := buffer.PeekNextID()
if expectedID < nextID {
log.Printf("------------WRONG ID, WILL WAIT FOR NEXT MESSAGE, expectedID %v < nextID: %v\n", expectedID, pd.ID)
return nil
}
log.Printf("------------CORRECT ID, EXPECTED: %v, GOT: %v\n", expectedID, pd.ID)
// Write the data to the channel that the listener is reading from.
for {
pdPopped, ok := buffer.Pop()
if !ok {
// Buffer is empty, break out, and wait for next message.
break
}
srcWriteCh <- pdPopped.Data
if !pd.OK {
log.Printf("error: portSrcSubProcFunc: pdd.OK is false. err=%v\n", pd.ErrorMsg)
return fmt.Errorf("%v", pd.ErrorMsg)
}
}
return nil
}()
if err != nil {
return err
}
}
}
}
return pf
}
// portDstSubProcFunc is the function that will be run by the portDstSubProc process.
// It will connect to the destination IP and port, and send messages to the source node
// to write the data to the source IP and port.
// It will also handle the incomming messages from the source node and write the
// data in the message to the destination IP and port.
func portDstSubProcFunc(pid portInitialData, message Message, cancel context.CancelFunc) func(context.Context, process, chan Message) error {
pf := func(ctx context.Context, proc process, procFuncCh chan Message) error {
defer cancel()
fmt.Printf("STARTED PROCFUNC: %v\n", proc.subject.name())
defer fmt.Println("portDstProcFunc: canceled procFunc", "processName", proc.processName)
// TODO: Start the tcp connection for the dst node here.
// ------------
conn, err := net.Dial("tcp", pid.DestinationIPAndPort)
if err != nil {
log.Fatalf("error: portDstSubProcFunc: dial failed. err=%v", err)
}
defer conn.Close()
// Read from destination network connection and send messages to src node of whats read.
go func() {
id := 0
for {
var errorMsg string
ok := true
b := make([]byte, 65535)
n, err := conn.Read(b)
if err != nil {
// If there was an error while reading, set the value of errorMsg and ok accordingly.
// This information will then be used on the src node to stop sub processes etc.
switch {
case err == io.EOF:
ok = false
errorMsg = fmt.Sprintf("portDstSubProcFunc: conn.Read() returned EOF, n: %v", n)
log.Printf("AT DST: %v\n", errorMsg)
case strings.Contains(err.Error(), "use of closed network connection"):
ok = false
fmt.Printf("AT DST: portDstSubProcFunc: conn.Read(): closed network connection, err: %v, n: %v\n", err, n)
default:
ok = false
fmt.Printf("AT DST: portDstSubProcFunc: conn.Read(): other error, err: %v, n: %v\n", err, n)
}
}
fmt.Printf("portDstSubProcFunc: read from network conn: length: %v\n", n)
pdd := portData{
OK: ok,
ErrorMsg: errorMsg,
Data: b[:n],
ID: id,
}
id++
cb, err := cbor.Marshal(pdd)
if err != nil {
log.Fatalf("error: portDstSubProcFunc: cbor marshalling failed. err=%v", err)
}
msg := Message{
ToNode: pid.SourceNode,
Method: Method(pid.SourceSubMethod),
Data: cb,
ACKTimeout: message.ACKTimeout,
Retries: message.Retries,
ReplyMethod: None,
}
proc.newMessagesCh <- msg
fmt.Printf(" ******* DST: Created message to send with id : %v\n", id)
// If there was en error while reading, we exit the loop, so the connection is closed.
if !ok {
// TODO: Check out the cancelation!!!
//cancel()
return
}
}
}()
// -----------------------------------------------------------------------------------
// Read from messages from src node and write to the destination network connection.
// -----------------------------------------------------------------------------------
expectedID := -1
buffer := NewPortSortedBuffer()
for {
select {
case <-ctx.Done():
fmt.Println("portDstProcFunc: got <-ctx.Done() cancelling procFunc", "processName", proc.processName)
return nil
// Pick up the message recived by the copySrcSubHandler.
case message := <-procFuncCh:
expectedID++
var pd portData
err := cbor.Unmarshal(message.Data, &pd)
if err != nil {
log.Fatalf("error: portDstSubProcFunc: cbor unmarshalling failed. err=%v", err)
}
fmt.Printf("<---- GOT DATA ON DST, id: %v, length: %v\n", pd.ID, len(pd.Data))
buffer.Push(pd)
err = func() error {
nextID, _ := buffer.PeekNextID()
// Messages might come out of order.
// If the expected id is larger than the next id, we've lost packages, and end the process.
// If the expected id is less than the next id, we return and wait for the next message.
// if expectedID > nextID {
// err := fmt.Errorf("portDstSubProcFunc: error: expectedID %v > nextID %v, we've probably lost packages", expectedID, nextID)
// log.Println(err)
//
// log.Printf(" ---------- DEBUG portDstSubProcFunc: buffer now contains: %+v\n", buffer.buffer)
//
// return err
// }
if expectedID < nextID {
log.Printf("------------WRONG ID, WILL WAIT FOR NEXT MESSAGE, expectedID %v < nextID: %v\n", expectedID, pd.ID)
return nil
}
log.Printf("------------CORRECT ID, EXPECTED: %v, GOT: %v\n", expectedID, pd.ID)
// Loop over eventual messages in the buffer and write them to the connection.
for {
pdPopped, ok := buffer.Pop()
if !ok {
// Buffer is empty, break out and wait for next message.
break
}
n, err := conn.Write(pdPopped.Data)
if err != nil {
err := fmt.Errorf("error: portDstSubProcFunc: conn.Write failed. err=%v", err)
log.Println(err)
return err
}
fmt.Printf("--------> conn: portDstSubProcFunc: wrote %v bytes to connection\n", n)
}
return nil
}()
// Check if there was an error in the above function.
if err != nil {
return err
}
}
}
}
return pf
}
// -----------------------------------------------------------
// portSortedBuffer is a thread-safe buffer that sorts the data by ID.
type portSortedBuffer struct {
buffer []portData
mu sync.Mutex
}
// NewPortSortedBuffer creates a new portSortedBuffer.
func NewPortSortedBuffer() *portSortedBuffer {
b := portSortedBuffer{}
return &b
}
// Push adds a new portData to the buffer and sorts it by ID.
func (b *portSortedBuffer) Push(value portData) {
b.buffer = append(b.buffer, value)
b.mu.Lock()
defer b.mu.Unlock()
sort.SliceStable(b.buffer, func(i, j int) bool {
return b.buffer[i].ID < b.buffer[j].ID
})
}
// Pop removes and returns the first portData from the buffer.
func (b *portSortedBuffer) Pop() (portData, bool) {
b.mu.Lock()
defer b.mu.Unlock()
if len(b.buffer) == 0 {
return portData{}, false
}
value := b.buffer[0]
b.buffer = b.buffer[1:]
return value, true
}
// PeekNextID returns the ID of the next portData in the buffer without removing it.
func (b *portSortedBuffer) PeekNextID() (int, bool) {
b.mu.Lock()
defer b.mu.Unlock()
if len(b.buffer) == 0 {
return -1, false
}
id := b.buffer[0].ID
return id, true
}