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:
parent
e1a3fdca05
commit
5127ab7099
11 changed files with 1716 additions and 2 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -17,3 +17,4 @@ doc/concept/via/README.md
|
|||
notes.txt
|
||||
toolbox/
|
||||
signing/
|
||||
frontend/
|
||||
|
|
301
doc/.$drawings.drawio.bkp
Normal file
301
doc/.$drawings.drawio.bkp
Normal 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<br><br>NB: should it be pushed to both ack'ed and not ack'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<div>detected in hello message</div>" 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<div>receive</div><div>(node)</div>" 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<div>(central)</div>" 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'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="<div>read session info,<br>and start dst sub process</div>" 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:<div><br></div><div>create session info from methodArgs like dst ip:port to connect to, and also uuid</div><div><br></div><div>startSub Process for src</div>" 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="<div>read messages from destination,</div><div>extract the data field, and put</div><div>it on the tcp connection to&nbsp;</div><div>source</div><br>read tcp stream from source<div>put them in a message and send</div><div>to destination</div>" 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<br>to create a sub process that<br>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<div>put them in a message and send</div><div>to source</div><div><br></div><div>read messages from source,</div><div>extract the data field, and put</div><div>it on the tcp connection to&nbsp;</div><div>destination</div>" 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<div>using dst port</div>" 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<br>sub process, methodPortSrc+UUID:<div><br></div>" 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<br>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="<span style="font-size: 11px; text-wrap: nowrap; background-color: rgb(24, 20, 29);">Start sub dst process</span>" 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:<div>toNode: node1</div><div>method: portsrc</div><div><br></div><div>methodArgs:</div><div>- dstNode: node2</div><div>-</div>" 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="<h1 style="margin-top: 0px;">Sending Data</h1><div><br></div><div>Add a structure to use in the data field of the message with 2 fields.</div><div>1. The actual data in bytes</div><div>2. Status messages like&nbsp; &nbsp; &nbsp; disconnect, done, etc...</div><div><br></div><div>With sub processes on both sides we check the status messages to understand what to do with the connection.</div>" 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<div style="font-size: 20px;">node1</div>" 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<div style="font-size: 20px;">node2</div>" 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&nbsp;<div style="font-size: 20px;">localhost:10022</div>" 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<div style="font-size: 20px;">192.168.1.99:22</div>" 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<div>Server</div>" 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<div style="font-size: 20px;">connect a tcp stream from node1 on tcp port 10022<br style="font-size: 20px;">to<br style="font-size: 20px;">node2 tcp 192.168.1.99:22<br>and allow tcp forwarding</div>" 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<div style="font-size: 20px;">connect to</div><div style="font-size: 20px;">localhost:10022</div>" 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<div style="font-size: 20px;">at</div><div style="font-size: 20px;">192.168.1:99</div>" 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="<font style="font-size: 30px;">SSH Connection</font>" 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
301
doc/drawings.drawio
Normal 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<br><br>NB: should it be pushed to both ack'ed and not ack'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<div>detected in hello message</div>" 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<div>receive</div><div>(node)</div>" 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<div>(central)</div>" 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'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="<div>read session info,<br>and start dst sub process</div>" 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:<div><br></div><div>create session info from methodArgs like dst ip:port to connect to, and also uuid</div><div><br></div><div>startSub Process for src</div>" 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="<div>read messages from destination,</div><div>extract the data field, and put</div><div>it on the tcp connection to&nbsp;</div><div>source</div><br>read tcp stream from source<div>put them in a message and send</div><div>to destination</div>" 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<br>to create a sub process that<br>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<div>put them in a message and send</div><div>to source</div><div><br></div><div>read messages from source,</div><div>extract the data field, and put</div><div>it on the tcp connection to&nbsp;</div><div>destination</div>" 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<div>using dst port</div>" 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<br>sub process, methodPortSrc+UUID:<div><br></div>" 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<br>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="<span style="font-size: 11px; text-wrap: nowrap; background-color: rgb(24, 20, 29);">Start sub dst process</span>" 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:<div>toNode: node1</div><div>method: portsrc</div><div><br></div><div>methodArgs:</div><div>- dstNode: node2</div><div>-</div>" 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="<h1 style="margin-top: 0px;">Sending Data</h1><div><br></div><div>Add a structure to use in the data field of the message with 2 fields.</div><div>1. The actual data in bytes</div><div>2. Status messages like&nbsp; &nbsp; &nbsp; disconnect, done, etc...</div><div><br></div><div>With sub processes on both sides we check the status messages to understand what to do with the connection.</div>" 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<div style="font-size: 20px;">node1</div>" 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<div style="font-size: 20px;">node2</div>" 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&nbsp;<div style="font-size: 20px;">localhost:10022</div>" 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<div style="font-size: 20px;">192.168.1.99:22</div>" 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<div>Server</div>" 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<div style="font-size: 20px;">connect a tcp stream from node1 on tcp port 10022<br style="font-size: 20px;">to<br style="font-size: 20px;">node2 tcp 192.168.1.99:22<br>and allow tcp forwarding</div>" 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<div style="font-size: 20px;">connect to</div><div style="font-size: 20px;">localhost:10022</div>" 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<div style="font-size: 20px;">at</div><div style="font-size: 20px;">192.168.1:99</div>" 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="<font style="font-size: 30px;">SSH Connection</font>" 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
4
doc/portforward-ssh.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 497 KiB |
335
doc/request_dummy.go.example_method
Normal file
335
doc/request_dummy.go.example_method
Normal 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
|
||||
}
|
|
@ -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)
|
||||
|
|
44
doc/src/usecase-ctrl-as-tcp-forwarder-for-ssh.md
Normal file
44
doc/src/usecase-ctrl-as-tcp-forwarder-for-ssh.md
Normal 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.
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
17
requests.go
17
requests.go
|
@ -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
708
requests_port.go
Normal 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
|
||||
}
|
Loading…
Add table
Reference in a new issue