Compare commits
115 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0623ed8a79 | ||
![]() |
964219c25f | ||
![]() |
23c45022b3 | ||
![]() |
c6d9c0e340 | ||
![]() |
460926afe8 | ||
![]() |
e278de3a66 | ||
![]() |
d1d2663c42 | ||
![]() |
5af3086205 | ||
![]() |
f97683379f | ||
![]() |
42d542dd8b | ||
![]() |
d94d9c4da0 | ||
![]() |
dddd8ea916 | ||
![]() |
b864407cc2 | ||
![]() |
646767e7bf | ||
![]() |
76e1e9af83 | ||
![]() |
6bd37c4d51 | ||
![]() |
e2f73efb10 | ||
![]() |
5fda56e88d | ||
![]() |
63833f24ab | ||
![]() |
0d10b5b516 | ||
![]() |
38ca0d08e7 | ||
![]() |
a9bf9faba1 | ||
![]() |
3c6a72d179 | ||
![]() |
e8b7525950 | ||
![]() |
a0407fd3e3 | ||
![]() |
3c5397e87f | ||
![]() |
9baee36493 | ||
![]() |
379de14264 | ||
![]() |
dc12ba56d4 | ||
![]() |
281084cb81 | ||
![]() |
ff6b890bfd | ||
![]() |
12d1bf38b2 | ||
![]() |
7fa46ffc98 | ||
![]() |
a516752491 | ||
![]() |
4c81f5f7a0 | ||
![]() |
a13b920f05 | ||
![]() |
e5d5a0360b | ||
![]() |
460bd1b8f4 | ||
![]() |
7d820eeaaa | ||
![]() |
ac54a46ded | ||
![]() |
22a3e9f3a7 | ||
![]() |
b7ce5c1061 | ||
![]() |
7729762ce9 | ||
![]() |
13bf63f35a | ||
![]() |
91fc13b151 | ||
![]() |
7cf9e8b61e | ||
![]() |
c7d6848930 | ||
![]() |
eabd2d94f6 | ||
![]() |
76924e5961 | ||
![]() |
027983674b | ||
![]() |
7521a7862a | ||
![]() |
c11b90dc47 | ||
![]() |
00969e92f7 | ||
![]() |
129579130d | ||
![]() |
b43178746e | ||
![]() |
72dc2b1d74 | ||
![]() |
0b74205adf | ||
![]() |
d4c9b8c57d | ||
![]() |
27e7865789 | ||
![]() |
5fd5f21be6 | ||
![]() |
d8c0f81c78 | ||
![]() |
291b43dd4b | ||
![]() |
98ddd74e32 | ||
![]() |
9d4fce4593 | ||
![]() |
87a4e609bb | ||
![]() |
4830ef434a | ||
![]() |
00cba613a1 | ||
![]() |
6e0cf375b3 | ||
![]() |
802a59bd65 | ||
![]() |
c9b61b818e | ||
![]() |
f794cd513f | ||
![]() |
6f94d156ec | ||
![]() |
6ebe813d0e | ||
![]() |
4694834222 | ||
![]() |
4a105ff77e | ||
![]() |
e5c90d06eb | ||
![]() |
4b77bc4d02 | ||
![]() |
e42d89a43c | ||
![]() |
376c392d58 | ||
![]() |
88c615b411 | ||
![]() |
4a8fc1acfe | ||
![]() |
6509b803bc | ||
![]() |
49533844d6 | ||
![]() |
09c260e7e1 | ||
![]() |
503c94e7e1 | ||
![]() |
cc91ed6bf0 | ||
![]() |
5ffafcd28d | ||
![]() |
c40b3af503 | ||
![]() |
abd13ace73 | ||
![]() |
4778044200 | ||
![]() |
bf2bef0f4b | ||
![]() |
d7af8dafa9 | ||
![]() |
2e7a301855 | ||
![]() |
56cc5bd017 | ||
![]() |
a537690baa | ||
![]() |
6ea277fc47 | ||
![]() |
d3c0ff150f | ||
![]() |
34baf7bec3 | ||
![]() |
ba1f9719c2 | ||
![]() |
8de30dc68f | ||
![]() |
430fd0b0bb | ||
![]() |
7b4edecf96 | ||
![]() |
2896c839bd | ||
![]() |
c69106a879 | ||
![]() |
ca8fd03818 | ||
![]() |
186b20306e | ||
![]() |
b06ef4ccf8 | ||
![]() |
f569145156 | ||
![]() |
193ce37241 | ||
![]() |
6cd8e18295 | ||
![]() |
69655d5d86 | ||
![]() |
299080f4c2 | ||
![]() |
a57ebd8e7e | ||
![]() |
aab53e9153 | ||
![]() |
90dcde3cac |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -20,3 +20,7 @@ _cgo_export.*
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
itp-golang-github-eyedeekay-gosam.txt
|
||||
.pc
|
||||
deb/
|
||||
samsocks/samsocks
|
||||
|
195
CONTRIBUTING.md
Normal file
195
CONTRIBUTING.md
Normal file
@@ -0,0 +1,195 @@
|
||||
How to make contributions to goSam
|
||||
==================================
|
||||
|
||||
Welcome to goSam, the easy-to-use http client for i2p. We're glad you're here
|
||||
and interested in contributing. Here's some help getting started.
|
||||
|
||||
Table of Contents
|
||||
-----------------
|
||||
|
||||
* (1) Environment
|
||||
* (2) Testing
|
||||
* (3) Filing Issues/Reporting Bugs/Making Suggestions
|
||||
* (4) Contributing Code/Style Guide
|
||||
- (a) Adding i2cp and tunnel Options
|
||||
- (b) Writing Tests
|
||||
- (c) Style
|
||||
- (d) Other kinds of modification?
|
||||
* (5) Conduct
|
||||
|
||||
### (1) Environment
|
||||
|
||||
goSam is a simple go library. You are free to use an IDE if you wish, but all
|
||||
that is required to build and test the library are a go compiler and the gofmt
|
||||
tool. Git is the version control system. All the files in the library are in a
|
||||
single root directory. Invoking go build from this directory not generate any
|
||||
files.
|
||||
|
||||
### (2) Testing
|
||||
|
||||
Tests are implemented using the standard go "testing" library in files named
|
||||
"file\_test.go," so tests of the client go in client\_test.go, name lookups
|
||||
in naming\_test.go, et cetera. Everything that can be tested, should be tested.
|
||||
|
||||
Testing is done by running
|
||||
|
||||
go test
|
||||
|
||||
More information about designing tests is below in the
|
||||
**Contributing Code/Style Guide** section below.
|
||||
|
||||
### (3) Filing issues/Reporting bugs/Making suggestions
|
||||
|
||||
If you discover the library doing something you don't think is right, please let
|
||||
us know! Just filing an issue here is OK.
|
||||
|
||||
If you need to suggest a feature, we're happy to hear from you too. Filing an
|
||||
issue will give us a place to discuss how it's implemented openly and publicly.
|
||||
|
||||
Please file an issue for your new code contributions in order to provide us with
|
||||
a place to discuss them for inclusion.
|
||||
|
||||
### (4) Contributing Code/Style Guide
|
||||
|
||||
Welcome new coders. We have good news for you, this library is really easy to
|
||||
contribute to. The easiest contributions take the form of i2cp and tunnel
|
||||
options.
|
||||
|
||||
#### (a) Adding i2cp and tunnel Options
|
||||
|
||||
First, add a variable to store the state of your new option. For example, the
|
||||
existing variables are in the Client class [here:](https://github.com/cryptix/goSam/blob/701d7fcf03ddb354262fe213163dcf6f202a24f1/client.go#L29)
|
||||
|
||||
i2cp and tunnel options are added in a highly uniform process of basically three
|
||||
steps. First, you create a functional argument in the options.go file, in the
|
||||
form:
|
||||
|
||||
``` Go
|
||||
// SetOPTION sets $OPTION
|
||||
func SetOPTION(arg type) func(*Client) error { // arg type
|
||||
return func(c *Client) error { // pass a client to the inner function and declare error return function
|
||||
if arg == valid { // validate the argument
|
||||
c.option = s // set the variable to the argument value
|
||||
return nil // if option is set successfully return nil error
|
||||
}
|
||||
return fmt.Errorf("Invalid argument:" arg) // return a descriptive error if arg is invalid
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
[example](https://github.com/cryptix/goSam/blob/701d7fcf03ddb354262fe213163dcf6f202a24f1/options.go#L187)
|
||||
|
||||
Next, you create a getter which prepares the option. Regardless of the type of
|
||||
option that is set, these must return strings representing valid i2cp options.
|
||||
|
||||
``` Go
|
||||
//return the OPTION as a string.
|
||||
func (c *Client) option() string {
|
||||
return fmt.Sprintf("i2cp.option=%d", c.option)
|
||||
}
|
||||
```
|
||||
|
||||
[example](https://github.com/cryptix/goSam/blob/701d7fcf03ddb354262fe213163dcf6f202a24f1/options.go#L299)
|
||||
|
||||
Lastly, you'll need to add it to the allOptions function and the
|
||||
Client.NewClient() function. To add it to allOptions, it looks like this:
|
||||
|
||||
``` Go
|
||||
//return all options as string ready for passing to sendcmd
|
||||
func (c *Client) allOptions() string {
|
||||
return c.inlength() + " " +
|
||||
c.outlength() + " " +
|
||||
... //other options removed from example for brevity
|
||||
c.option()
|
||||
}
|
||||
```
|
||||
|
||||
``` Go
|
||||
//return all options as string ready for passing to sendcmd
|
||||
func (c *Client) NewClient() (*Client, error) {
|
||||
return NewClientFromOptions(
|
||||
SetHost(c.host),
|
||||
SetPort(c.port),
|
||||
... //other options removed from example for brevity
|
||||
SetCompression(c.compression),
|
||||
setlastaddr(c.lastaddr),
|
||||
setid(c.id),
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
[example](https://github.com/cryptix/goSam/blob/701d7fcf03ddb354262fe213163dcf6f202a24f1/options.go#L333)
|
||||
|
||||
#### (b) Writing Tests
|
||||
|
||||
Before the feature can be added, you'll need to add a test for it to
|
||||
options_test.go. To do this, just add your new option to the long TestOptions
|
||||
functions in options_test.go.
|
||||
|
||||
``` Go
|
||||
func TestOptionHost(t *testing.T) {
|
||||
client, err := NewClientFromOptions(
|
||||
SetHost("127.0.0.1"),
|
||||
SetPort("7656"),
|
||||
... //other options removed from example for brevity
|
||||
SetCloseIdleTime(300001),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("NewClientFromOptions() Error: %q\n", err)
|
||||
}
|
||||
if result, err := client.validCreate(); err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
} else {
|
||||
t.Log(result)
|
||||
}
|
||||
client.CreateStreamSession("")
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptionPortInt(t *testing.T) {
|
||||
client, err := NewClientFromOptions(
|
||||
SetHost("127.0.0.1"),
|
||||
SetPortInt(7656),
|
||||
... //other options removed from example for brevity
|
||||
SetUnpublished(true),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("NewClientFromOptions() Error: %q\n", err)
|
||||
}
|
||||
if result, err := client.validCreate(); err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
} else {
|
||||
t.Log(result)
|
||||
}
|
||||
client.CreateStreamSession("")
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
If any of these tasks fail, then the test should fail.
|
||||
|
||||
#### (c) Style
|
||||
|
||||
It's pretty simple to make sure the code style is right, just run gofmt over it
|
||||
to adjust the indentation, and golint over it to ensure that your comments are
|
||||
of the correct form for the documentation generator.
|
||||
|
||||
#### (d) Other kinds of modification?
|
||||
|
||||
It may be useful to extend goSam in other ways. Since there's not a
|
||||
one-size-fits-all uniform way of dealing with these kinds of changes, open an
|
||||
issue for discussion and
|
||||
|
||||
### (5) Conduct
|
||||
|
||||
This is a small-ish, straightforward library intended to enable a clear
|
||||
technical task. We should be able to be civil with eachother, and give and
|
||||
accept criticism contructively and respectfully.
|
||||
|
||||
This document was drawn from the examples given by Mozilla
|
||||
[here](mozillascience.github.io/working-open-workshop/contributing/)
|
351
LICENSE
351
LICENSE
@@ -1,339 +1,22 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
Copyright (c) 2014 Henry
|
||||
|
||||
Preamble
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
{description}
|
||||
Copyright (C) {year} {fullname}
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
{signature of Ty Coon}, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
|
27
Makefile
Normal file
27
Makefile
Normal file
@@ -0,0 +1,27 @@
|
||||
|
||||
USER_GH=eyedeekay
|
||||
VERSION=0.32.5
|
||||
packagename=gosam
|
||||
|
||||
echo: fmt
|
||||
@echo "type make version to do release $(VERSION)"
|
||||
|
||||
version:
|
||||
gothub release -s $(GITHUB_TOKEN) -u $(USER_GH) -r $(packagename) -t v$(VERSION) -d "version $(VERSION)"
|
||||
|
||||
del:
|
||||
gothub delete -s $(GITHUB_TOKEN) -u $(USER_GH) -r $(packagename) -t v$(VERSION)
|
||||
|
||||
tar:
|
||||
tar --exclude .git \
|
||||
--exclude .go \
|
||||
--exclude bin \
|
||||
--exclude examples \
|
||||
-cJvf ../$(packagename)_$(VERSION).orig.tar.xz .
|
||||
|
||||
link:
|
||||
rm -f ../goSam
|
||||
ln -sf . ../goSam
|
||||
|
||||
fmt:
|
||||
gofmt -w -s *.go */*.go
|
79
README.md
79
README.md
@@ -1,18 +1,21 @@
|
||||
goSam
|
||||
=====
|
||||
|
||||
A go library for using the [I2P](https://geti2p.net/en/) Simple Anonymous Messaging ([SAM version 3.0](https://geti2p.net/en/docs/api/samv3)) bridge
|
||||
A go library for using the [I2P](https://geti2p.net/en/) Simple Anonymous
|
||||
Messaging ([SAM version 3.0](https://geti2p.net/en/docs/api/samv3)) bridge. It
|
||||
has support for all streaming features SAM version 3.2.
|
||||
|
||||
This is in an **early development stage**. I would love to hear about any issues or ideas for improvement.
|
||||
This is widely used and easy to use, but thusfar, mostly by me. It sees a lot of
|
||||
testing and no breaking changes to the API are expected.
|
||||
|
||||
## Installation
|
||||
```
|
||||
go get github.com/cryptix/goSam
|
||||
go get github.com/eyedeekay/goSam
|
||||
```
|
||||
|
||||
## Using it for HTTP Transport
|
||||
|
||||
I implemented `Client.Dial` like `net.Dial` so you can use go's library packages like http.
|
||||
`Client.Dial` implements `net.Dial` so you can use go's library packages like http.
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -67,7 +70,71 @@ func checkErr(err error) {
|
||||
}
|
||||
```
|
||||
|
||||
## Using it as a SOCKS proxy
|
||||
|
||||
`client` also implements a resolver compatible with
|
||||
[`getlantern/go-socks5`](https://github.com/getlantern/go-socks5),
|
||||
making it very easy to implement a SOCKS5 server.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
|
||||
"github.com/eyedeekay/goSam"
|
||||
"github.com/getlantern/go-socks5"
|
||||
"log"
|
||||
)
|
||||
|
||||
var (
|
||||
samaddr = flag.String("sam", "127.0.0.1:7656", "SAM API address to use")
|
||||
socksaddr = flag.String("socks", "127.0.0.1:7675", "SOCKS address to use")
|
||||
)
|
||||
|
||||
func main() {
|
||||
sam, err := goSam.NewClient(*samaddr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
log.Println("Client Created")
|
||||
|
||||
// create a transport that uses SAM to dial TCP Connections
|
||||
conf := &socks5.Config{
|
||||
Dial: sam.DialContext,
|
||||
Resolver: sam,
|
||||
}
|
||||
server, err := socks5.New(conf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Create SOCKS5 proxy on localhost port 8000
|
||||
if err := server.ListenAndServe("tcp", *socksaddr); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### .deb package
|
||||
|
||||
A package for installing this on Debian is buildable, and a version for Ubuntu
|
||||
is available as a PPA and mirrored via i2p. To build the deb package, from the
|
||||
root of this repository with the build dependencies installed(git, i2p, go,
|
||||
debuild) run the command
|
||||
|
||||
debuild -us -uc
|
||||
|
||||
to produce an unsigned deb for personal use only. For packagers,
|
||||
|
||||
debuild -S
|
||||
|
||||
will produce a viable source package for use with Launchpad PPA's and other
|
||||
similar systems.
|
||||
|
||||
### TODO
|
||||
|
||||
* Implement `STREAM ACCEPT` and `STREAM FORWARD`
|
||||
* Implement datagrams (Repliable and Anon)
|
||||
* Improve recovery on failed sockets
|
||||
* Implement `STREAM FORWARD`
|
||||
* Implement datagrams (Repliable and Anon)
|
||||
|
||||
|
53
accept.go
53
accept.go
@@ -3,34 +3,61 @@ package goSam
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/cryptix/go/debug"
|
||||
)
|
||||
|
||||
// Accept creates a new Client and accepts a connection on it
|
||||
func (c *Client) Accept() (net.Conn, error) {
|
||||
id, newAddr, err := c.CreateStreamSession("")
|
||||
// AcceptI2P creates a new Client and accepts a connection on it
|
||||
func (c *Client) AcceptI2P() (net.Conn, error) {
|
||||
listener, err := c.Listen()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return listener.Accept()
|
||||
}
|
||||
|
||||
// Listen creates a new Client and returns a net.listener which *must* be started
|
||||
// with Accept
|
||||
func (c *Client) Listen() (net.Listener, error) {
|
||||
return c.ListenI2P(c.destination)
|
||||
}
|
||||
|
||||
// ListenI2P creates a new Client and returns a net.listener which *must* be started
|
||||
// with Accept
|
||||
func (c *Client) ListenI2P(dest string) (net.Listener, error) {
|
||||
var err error
|
||||
c.destination, err = c.CreateStreamSession(dest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Println("NewAddr:", newAddr)
|
||||
|
||||
newC, err := NewDefaultClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if c.d == nil {
|
||||
c.d, err = c.NewClient(c.NewID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("Listening on destination:", c.Base32()+".b32.i2p")
|
||||
|
||||
if c.debug {
|
||||
newC.SamConn = debug.WrapConn(newC.SamConn)
|
||||
c.SamConn = WrapConn(c.SamConn)
|
||||
}
|
||||
|
||||
resp, err := newC.StreamAccept(id)
|
||||
return c.d, nil
|
||||
}
|
||||
|
||||
// Accept accepts a connection on a listening goSam.Client(Implements net.Listener)
|
||||
// or, if the connection isn't listening yet, just calls AcceptI2P for compatibility
|
||||
// with older versions.
|
||||
func (c *Client) Accept() (net.Conn, error) {
|
||||
if c.id == 0 {
|
||||
return c.AcceptI2P()
|
||||
}
|
||||
resp, err := c.StreamAccept()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Println("Accept Resp:", resp)
|
||||
|
||||
return newC.SamConn, nil
|
||||
return c.SamConn, nil
|
||||
}
|
||||
|
172
client.go
172
client.go
@@ -2,19 +2,35 @@ package goSam
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/sha256"
|
||||
"encoding/base32"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/cryptix/go/debug"
|
||||
samkeys "github.com/eyedeekay/goSam/compat"
|
||||
)
|
||||
|
||||
// A Client represents a single Connection to the SAM bridge
|
||||
type Client struct {
|
||||
host string
|
||||
port string
|
||||
host string
|
||||
port string
|
||||
fromport string
|
||||
toport string
|
||||
|
||||
SamConn net.Conn
|
||||
rd *bufio.Reader
|
||||
SamConn net.Conn // Control socket
|
||||
SamDGConn DatagramConn // Datagram socket
|
||||
rd *bufio.Reader
|
||||
d *Client
|
||||
|
||||
sigType string
|
||||
destination string
|
||||
|
||||
inLength uint
|
||||
inVariance int
|
||||
@@ -28,6 +44,7 @@ type Client struct {
|
||||
|
||||
dontPublishLease bool
|
||||
encryptLease bool
|
||||
leaseSetEncType string
|
||||
|
||||
reduceIdle bool
|
||||
reduceIdleTime uint
|
||||
@@ -36,9 +53,36 @@ type Client struct {
|
||||
closeIdle bool
|
||||
closeIdleTime uint
|
||||
|
||||
compression bool
|
||||
|
||||
debug bool
|
||||
mutex sync.Mutex
|
||||
//NEVER, EVER modify lastaddr or id yourself. They are used internally only.
|
||||
id int32
|
||||
sammin int
|
||||
sammax int
|
||||
}
|
||||
|
||||
// SAMsigTypes is a slice of the available signature types
|
||||
var SAMsigTypes = []string{
|
||||
"SIGNATURE_TYPE=DSA_SHA1",
|
||||
"SIGNATURE_TYPE=ECDSA_SHA256_P256",
|
||||
"SIGNATURE_TYPE=ECDSA_SHA384_P384",
|
||||
"SIGNATURE_TYPE=ECDSA_SHA512_P521",
|
||||
"SIGNATURE_TYPE=EdDSA_SHA512_Ed25519",
|
||||
}
|
||||
|
||||
var ValidSAMCommands = []string{
|
||||
"HELLO",
|
||||
"SESSION",
|
||||
"STREAM",
|
||||
}
|
||||
|
||||
var (
|
||||
i2pB64enc *base64.Encoding = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~")
|
||||
i2pB32enc *base32.Encoding = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567")
|
||||
)
|
||||
|
||||
// NewDefaultClient creates a new client, connecting to the default host:port at localhost:7656
|
||||
func NewDefaultClient() (*Client, error) {
|
||||
return NewClient("localhost:7656")
|
||||
@@ -49,6 +93,49 @@ func NewClient(addr string) (*Client, error) {
|
||||
return NewClientFromOptions(SetAddr(addr))
|
||||
}
|
||||
|
||||
// NewID generates a random number to use as an tunnel name
|
||||
func (c *Client) NewID() int32 {
|
||||
if c.id == 0 {
|
||||
c.id = rand.Int31n(math.MaxInt32)
|
||||
fmt.Printf("Initializing new ID: %d\n", c.id)
|
||||
}
|
||||
return c.id
|
||||
}
|
||||
|
||||
// Destination returns the full destination of the local tunnel
|
||||
func (c *Client) Destination() string {
|
||||
return c.destination
|
||||
}
|
||||
|
||||
// Base32 returns the base32 of the local tunnel
|
||||
func (c *Client) Base32() string {
|
||||
// hash := sha256.New()
|
||||
b64, err := i2pB64enc.DecodeString(c.Base64())
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
//hash.Write([]byte(b64))
|
||||
var s []byte
|
||||
for _, e := range sha256.Sum256(b64) {
|
||||
s = append(s, e)
|
||||
}
|
||||
return strings.ToLower(strings.Replace(i2pB32enc.EncodeToString(s), "=", "", -1))
|
||||
}
|
||||
|
||||
func (c *Client) base64() []byte {
|
||||
if c.destination != "" {
|
||||
s, _ := i2pB64enc.DecodeString(c.destination)
|
||||
alen := binary.BigEndian.Uint16(s[385:387])
|
||||
return s[:387+alen]
|
||||
}
|
||||
return []byte("")
|
||||
}
|
||||
|
||||
// Base64 returns the base64 of the local tunnel
|
||||
func (c *Client) Base64() string {
|
||||
return i2pB64enc.EncodeToString(c.base64())
|
||||
}
|
||||
|
||||
// NewClientFromOptions creates a new client, connecting to a specified port
|
||||
func NewClientFromOptions(opts ...func(*Client) error) (*Client, error) {
|
||||
var c Client
|
||||
@@ -56,37 +143,64 @@ func NewClientFromOptions(opts ...func(*Client) error) (*Client, error) {
|
||||
c.port = "7656"
|
||||
c.inLength = 3
|
||||
c.inVariance = 0
|
||||
c.inQuantity = 4
|
||||
c.inBackups = 2
|
||||
c.inQuantity = 3
|
||||
c.inBackups = 1
|
||||
c.outLength = 3
|
||||
c.outVariance = 0
|
||||
c.outQuantity = 4
|
||||
c.outBackups = 2
|
||||
c.outQuantity = 3
|
||||
c.outBackups = 1
|
||||
c.dontPublishLease = true
|
||||
c.encryptLease = false
|
||||
c.reduceIdle = false
|
||||
c.reduceIdleTime = 300000
|
||||
c.reduceIdleQuantity = 4
|
||||
c.reduceIdleQuantity = 1
|
||||
c.closeIdle = true
|
||||
c.closeIdleTime = 600000
|
||||
c.debug = false
|
||||
c.sigType = SAMsigTypes[4]
|
||||
c.id = 0
|
||||
c.destination = ""
|
||||
c.leaseSetEncType = "4,0"
|
||||
c.fromport = ""
|
||||
c.toport = ""
|
||||
c.sammin = 0
|
||||
c.sammax = 1
|
||||
for _, o := range opts {
|
||||
if err := o(&c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
conn, err := net.Dial("tcp", c.samaddr())
|
||||
c.id = c.NewID()
|
||||
conn, err := net.DialTimeout("tcp", c.samaddr(), 15*time.Minute)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if c.debug {
|
||||
conn = debug.WrapConn(conn)
|
||||
conn = WrapConn(conn)
|
||||
}
|
||||
c.SamConn = conn
|
||||
c.rd = bufio.NewReader(conn)
|
||||
return &c, c.hello()
|
||||
}
|
||||
|
||||
// ID returns a the current ID of the client as a string
|
||||
func (p *Client) ID() string {
|
||||
return fmt.Sprintf("%d", p.NewID())
|
||||
}
|
||||
|
||||
// Addr returns the address of the client as a net.Addr
|
||||
func (p *Client) Addr() net.Addr {
|
||||
keys, err := samkeys.DestToKeys(p.Destination())
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return keys.Addr()
|
||||
}
|
||||
|
||||
func (p *Client) LocalAddr() net.Addr {
|
||||
return p.Addr()
|
||||
}
|
||||
|
||||
//return the combined host:port of the SAM bridge
|
||||
func (c *Client) samaddr() string {
|
||||
return fmt.Sprintf("%s:%s", c.host, c.port)
|
||||
@@ -94,16 +208,16 @@ func (c *Client) samaddr() string {
|
||||
|
||||
// send the initial handshake command and check that the reply is ok
|
||||
func (c *Client) hello() error {
|
||||
r, err := c.sendCmd("HELLO VERSION MIN=3.0 MAX=3.2\n")
|
||||
r, err := c.sendCmd("HELLO VERSION MIN=3.%d MAX=3.%d\n", c.sammin, c.sammax)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if r.Topic != "HELLO" {
|
||||
return fmt.Errorf("Unknown Reply: %+v\n", r)
|
||||
return fmt.Errorf("Client Hello Unknown Reply: %+v\n", r)
|
||||
}
|
||||
|
||||
if r.Pairs["RESULT"] != "OK" || r.Pairs["VERSION"] != "3.0" {
|
||||
if r.Pairs["RESULT"] != "OK" {
|
||||
return fmt.Errorf("Handshake did not succeed\nReply:%+v\n", r)
|
||||
}
|
||||
|
||||
@@ -129,3 +243,31 @@ func (c *Client) Close() error {
|
||||
c.rd = nil
|
||||
return c.SamConn.Close()
|
||||
}
|
||||
|
||||
// NewClient generates an exact copy of the client with the same options, but
|
||||
// re-does all the handshaky business so that Dial can pick up right where it
|
||||
// left off, should the need arise.
|
||||
func (c *Client) NewClient(id int32) (*Client, error) {
|
||||
return NewClientFromOptions(
|
||||
SetHost(c.host),
|
||||
SetPort(c.port),
|
||||
SetDebug(c.debug),
|
||||
SetInLength(c.inLength),
|
||||
SetOutLength(c.outLength),
|
||||
SetInVariance(c.inVariance),
|
||||
SetOutVariance(c.outVariance),
|
||||
SetInQuantity(c.inQuantity),
|
||||
SetOutQuantity(c.outQuantity),
|
||||
SetInBackups(c.inBackups),
|
||||
SetOutBackups(c.outBackups),
|
||||
SetUnpublished(c.dontPublishLease),
|
||||
SetEncrypt(c.encryptLease),
|
||||
SetReduceIdle(c.reduceIdle),
|
||||
SetReduceIdleTime(c.reduceIdleTime),
|
||||
SetReduceIdleQuantity(c.reduceIdleQuantity),
|
||||
SetCloseIdle(c.closeIdle),
|
||||
SetCloseIdleTime(c.closeIdleTime),
|
||||
SetCompression(c.compression),
|
||||
setid(id),
|
||||
)
|
||||
}
|
||||
|
102
client_test.go
102
client_test.go
@@ -1,27 +1,111 @@
|
||||
//go:build nettest
|
||||
// +build nettest
|
||||
|
||||
package goSam
|
||||
|
||||
import "testing"
|
||||
|
||||
var client *Client
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"time"
|
||||
//"log"
|
||||
"net/http"
|
||||
|
||||
func setup(t *testing.T) {
|
||||
var err error
|
||||
"github.com/eyedeekay/sam3/helper"
|
||||
"github.com/eyedeekay/sam3/i2pkeys"
|
||||
)
|
||||
|
||||
// these tests expect a running SAM brige on this address
|
||||
client, err = NewClientFromOptions(SetDebug(true))
|
||||
func HelloServer(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
|
||||
}
|
||||
|
||||
func TestCompositeClient(t *testing.T) {
|
||||
listener, err := sam.I2PListener("testservice"+fmt.Sprintf("%d", rand.Int31n(math.MaxInt32)), "127.0.0.1:7656", "testkeys")
|
||||
if err != nil {
|
||||
t.Fatalf("Listener() Error: %q\n", err)
|
||||
}
|
||||
defer listener.Close()
|
||||
http.HandleFunc("/", HelloServer)
|
||||
go http.Serve(listener, nil)
|
||||
|
||||
listener2, err := sam.I2PListener("testservice"+fmt.Sprintf("%d", rand.Int31n(math.MaxInt32)), "127.0.0.1:7656", "testkeys2")
|
||||
if err != nil {
|
||||
t.Fatalf("Listener() Error: %q\n", err)
|
||||
}
|
||||
defer listener2.Close()
|
||||
// http.HandleFunc("/", HelloServer)
|
||||
go http.Serve(listener2, nil)
|
||||
|
||||
listener3, err := sam.I2PListener("testservice"+fmt.Sprintf("%d", rand.Int31n(math.MaxInt32)), "127.0.0.1:7656", "testkeys3")
|
||||
if err != nil {
|
||||
t.Fatalf("Listener() Error: %q\n", err)
|
||||
}
|
||||
defer listener3.Close()
|
||||
// http.HandleFunc("/", HelloServer)
|
||||
go http.Serve(listener3, nil)
|
||||
|
||||
sam, err := NewClientFromOptions(SetDebug(true))
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
}
|
||||
tr := &http.Transport{
|
||||
Dial: sam.Dial,
|
||||
}
|
||||
client := &http.Client{Transport: tr}
|
||||
defer sam.Close()
|
||||
time.Sleep(time.Second * 30)
|
||||
go func() {
|
||||
resp, err := client.Get("http://" + listener.Addr().(i2pkeys.I2PAddr).Base32())
|
||||
if err != nil {
|
||||
t.Fatalf("Get Error: %q\n", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
}()
|
||||
time.Sleep(time.Second * 15)
|
||||
go func() {
|
||||
resp, err := client.Get("http://" + listener2.Addr().(i2pkeys.I2PAddr).Base32())
|
||||
if err != nil {
|
||||
t.Fatalf("Get Error: %q\n", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
}()
|
||||
time.Sleep(time.Second * 15)
|
||||
go func() {
|
||||
resp, err := client.Get("http://" + listener3.Addr().(i2pkeys.I2PAddr).Base32())
|
||||
if err != nil {
|
||||
t.Fatalf("Get Error: %q\n", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
}()
|
||||
|
||||
time.Sleep(time.Second * 45)
|
||||
}
|
||||
|
||||
func teardown(t *testing.T) {
|
||||
func TestClientHello(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetDebug(true))
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
}
|
||||
t.Log(client.Base32())
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientHello(t *testing.T) {
|
||||
setup(t)
|
||||
teardown(t)
|
||||
func TestNewDestination(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetDebug(true))
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
}
|
||||
t.Log(client.Base32())
|
||||
if s, p, err := client.NewDestination(SAMsigTypes[3]); err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
t.Log(s, p)
|
||||
}
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
}
|
||||
|
23
compat/compat.go
Normal file
23
compat/compat.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package samkeys
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/eyedeekay/sam3/i2pkeys"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func DestToKeys(dest string) (i2pkeys.I2PKeys, error) {
|
||||
addr, err := i2pkeys.NewI2PAddrFromString(dest)
|
||||
if err != nil {
|
||||
return i2pkeys.I2PKeys{}, err
|
||||
}
|
||||
return i2pkeys.NewKeys(addr, dest), nil
|
||||
}
|
||||
|
||||
func KeysToDest(keys i2pkeys.I2PKeys) (string, error) {
|
||||
pksk := strings.SplitN(keys.String(), "\n", 2)
|
||||
if len(pksk) != 2 {
|
||||
return "", fmt.Errorf("Error converting from keys to destination")
|
||||
}
|
||||
return pksk[1], nil
|
||||
}
|
77
conn.go
Normal file
77
conn.go
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Henry
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
package goSam
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Conn Read data from the connection, writes data to te connection
|
||||
// and logs the data in-between.
|
||||
type Conn struct {
|
||||
RWC
|
||||
conn net.Conn
|
||||
}
|
||||
|
||||
// WrapConn wraps a net.Conn in a Conn.
|
||||
func WrapConn(c net.Conn) *Conn {
|
||||
wrap := Conn{
|
||||
conn: c,
|
||||
}
|
||||
wrap.Reader = NewReadLogger("<", c)
|
||||
wrap.Writer = NewWriteLogger(">", c)
|
||||
wrap.RWC.c = c
|
||||
return &wrap
|
||||
}
|
||||
|
||||
// LocalAddr returns the local address of the connection.
|
||||
func (c *Conn) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
// RemoteAddr returns the remote address of the connection.
|
||||
func (c *Conn) RemoteAddr() net.Addr {
|
||||
return c.conn.RemoteAddr()
|
||||
}
|
||||
|
||||
// SetDeadline sets the read and write deadlines associated with the connection
|
||||
func (c *Conn) SetDeadline(t time.Time) error {
|
||||
log.Println("WARNING: SetDeadline() not sure this works")
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
// SetReadDeadline sets the read deadline associated with the connection
|
||||
func (c *Conn) SetReadDeadline(t time.Time) error {
|
||||
log.Println("WARNING: SetReadDeadline() not sure this works")
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
// SetWriteDeadline sets the write deadline associated with the connection
|
||||
func (c *Conn) SetWriteDeadline(t time.Time) error {
|
||||
log.Println("WARNING: SetWriteDeadline() not sure this works")
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
26
datagram.go
Normal file
26
datagram.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package goSam
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DatagramConn
|
||||
type DatagramConn interface {
|
||||
ReadFrom(p []byte) (n int, addr net.Addr, err error)
|
||||
Read(b []byte) (n int, err error)
|
||||
WriteTo(p []byte, addr net.Addr) (n int, err error)
|
||||
Write(b []byte) (n int, err error)
|
||||
Close() error
|
||||
LocalAddr() net.Addr
|
||||
RemoteAddr() net.Addr
|
||||
SetDeadline(t time.Time) error
|
||||
SetReadDeadline(t time.Time) error
|
||||
SetWriteDeadline(t time.Time) error
|
||||
}
|
||||
|
||||
/**
|
||||
var conn DatagramConn = &Client{}
|
||||
|
||||
|
||||
*/
|
114
debian/changelog
vendored
Normal file
114
debian/changelog
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
golang-github-eyedeekay-gosam (0.32.30) UNRELEASED; urgency=medium
|
||||
|
||||
* Improve the defaults
|
||||
* Handle errors more correctly
|
||||
* Gracefully handle hangups by creating a new session
|
||||
* Set empty destinations to TRANSIENT when dialing
|
||||
|
||||
-- idk <hankhill19580@gmail.com> Sun, 03 Nov 2020 16:12:04 -0500
|
||||
|
||||
golang-github-eyedeekay-gosam (0.32.29) UNRELEASED; urgency=medium
|
||||
|
||||
* Maintenance updates
|
||||
|
||||
-- idk <hankhill19580@gmail.com> Mon, 23 Nov 2020 20:40:40 -0500
|
||||
|
||||
golang-github-eyedeekay-gosam (0.32.28) UNRELEASED; urgency=medium
|
||||
|
||||
* Maintenance updates
|
||||
|
||||
-- idk <hankhill19580@gmail.com> Thu, 12 Sept 2020 22:44:27 -0500
|
||||
|
||||
golang-github-eyedeekay-gosam (0.32.27) UNRELEASED; urgency=medium
|
||||
|
||||
* Add a Resolve function to fulfill SOCKS5 proxy requirements
|
||||
|
||||
-- idk <hankhill19580@gmail.com> Sun, 13 Sept 2020 04:48:27 -0500
|
||||
|
||||
golang-github-eyedeekay-gosam (0.32.26) UNRELEASED; urgency=medium
|
||||
|
||||
* Fix mistaken-identity issue with listeners
|
||||
|
||||
-- idk <hankhill19580@gmail.com> Thu, 03 Sept 2020 08:17:40 -0500
|
||||
|
||||
golang-github-eyedeekay-gosam (0.32.25) UNRELEASED; urgency=medium
|
||||
|
||||
* Support dual-keys by default in all future versions
|
||||
|
||||
-- idk <hankhill19580@gmail.com> Thu, 03 Sept 2020 04:25:04 -0500
|
||||
|
||||
golang-github-eyedeekay-gosam (0.32.24) UNRELEASED; urgency=medium
|
||||
|
||||
* Improve the mutex thingy
|
||||
|
||||
-- idk <hankhill19580@gmail.com> Tue, 25 Aug 2020 04:52:11 -0500
|
||||
|
||||
golang-github-eyedeekay-gosam (0.32.23) UNRELEASED; urgency=medium
|
||||
|
||||
* Protect Dial with a mutex to fix a lookup bug
|
||||
|
||||
-- idk <hankhill19580@gmail.com> Tue, 25 Aug 2020 10:29:26 -0500
|
||||
|
||||
golang-github-eyedeekay-gosam (0.3.2.1) bionic; urgency=medium
|
||||
|
||||
* Get rid of the debug directory, just move it into the source
|
||||
* Get rid of the old example, just use the one in the README
|
||||
|
||||
-- idk <hankhill19580@gmail.com> Sat, 08 Dec 2019 19:11:41 -0500
|
||||
|
||||
golang-github-eyedeekay-gosam (0.3.2.0) bionic; urgency=medium
|
||||
|
||||
* Enable persistent destinations
|
||||
* Make Base32 and Base64 addresses retrievable from client
|
||||
* bug fixes
|
||||
|
||||
-- idk <hankhill19580@gmail.com> Fri, 18 May 2019 18:12:21 -0500
|
||||
|
||||
golang-github-eyedeekay-gosam (0.1.1) bionic; urgency=medium
|
||||
|
||||
* Incorporate all the recent bug-fixes and improvements and stabilize.
|
||||
|
||||
-- idk <hankhill19580@gmail.com> Fri, 15 Mar 2019 14:46:21 -0500
|
||||
|
||||
golang-github-eyedeekay-gosam (0.1.0+git20190221.2896c83ubuntu1+nmu2ubuntu1) bionic; urgency=medium
|
||||
|
||||
* only run the offline tests by default
|
||||
|
||||
-- idk <hankhill19580@gmail.com> Thu, 28 Feb 2019 20:35:51 -0500
|
||||
|
||||
golang-github-eyedeekay-gosam (0.1.0+git20190221.2896c83ubuntu1+nmu2) bionic; urgency=medium
|
||||
|
||||
[ idk ]
|
||||
* incorporate cryptix-debug without the counter
|
||||
* incorporate cryptix-debug without the counter
|
||||
* fix module
|
||||
|
||||
-- idk <hankhill19580@gmail.com> Thu, 28 Feb 2019 20:32:58 -0500
|
||||
|
||||
golang-github-eyedeekay-gosam (0.1.0+git20190221.2896c83ubuntu1+nmu1) bionic; urgency=medium
|
||||
|
||||
* add sid
|
||||
*
|
||||
|
||||
-- idk <hankhill19580@gmail.com> Thu, 28 Feb 2019 18:52:01 -0500
|
||||
|
||||
golang-github-eyedeekay-gosam (0.1.0+git20190221.2896c83ubuntu1) bionic; urgency=medium
|
||||
|
||||
[ idk ]
|
||||
* Update changelog for 0.1.0+git20190221.2896c83-1 release
|
||||
* add signature type support
|
||||
* Don't check version if handshake succeeds, just accept that OK is OK
|
||||
* Don't check version if handshake succeeds, just accept that OK is OK
|
||||
* make dialer context aware and avoid redundant session create's
|
||||
* make dialer context aware and avoid redundant session create's
|
||||
* correct CONTRIBUTING.md
|
||||
* correct CONTRIBUTING.md
|
||||
* correct CONTRIBUTING.md
|
||||
* explicitly et invalid initial lastaddr
|
||||
* remove 'commented-out' id
|
||||
* Initial release (Closes: TODO)
|
||||
|
||||
[ idk ]
|
||||
* add bionic
|
||||
|
||||
-- idk <hankhill19580@gmail.com> Thu, 28 Feb 2019 18:51:23 -0500
|
1
debian/compat
vendored
Normal file
1
debian/compat
vendored
Normal file
@@ -0,0 +1 @@
|
||||
11
|
27
debian/control
vendored
Normal file
27
debian/control
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Source: golang-github-eyedeekay-gosam
|
||||
Section: devel
|
||||
Priority: optional
|
||||
Maintainer: idk <hankhill19580@gmail.com>
|
||||
Uploaders: idk <hankhill19580@gmail.com>
|
||||
Build-Depends: debhelper (>= 11),
|
||||
dh-golang,
|
||||
golang-any,
|
||||
i2pd | i2p,
|
||||
git,
|
||||
Standards-Version: 4.2.1
|
||||
Homepage: https://github.com/eyedeekay/gosam
|
||||
Vcs-Browser: https:/github.com/eyedeekay/gosam
|
||||
Vcs-Git: https://github.com/eyedeekay/gosam.git
|
||||
XS-Go-Import-Path: github.com/eyedeekay/gosam
|
||||
Testsuite: autopkgtest-pkg-go
|
||||
Launchpad-Bugs-Fixed: #1818159
|
||||
|
||||
Package: golang-github-eyedeekay-gosam-dev
|
||||
Architecture: all
|
||||
Depends: ${misc:Depends},
|
||||
i2pd | i2p
|
||||
Description: A go library for using the I2P (https://geti2p.net/en/)
|
||||
Simple Anonymous Messaging (SAM version 3.1
|
||||
(https://geti2p.net/en/docs/api/samv3)) bridge
|
||||
.
|
||||
Launchpad-Bugs-Fixed: #1818159
|
22
debian/copyright
vendored
Normal file
22
debian/copyright
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: gosam
|
||||
Source: https://github.com/eyedeekay/gosam
|
||||
Files-Excluded:
|
||||
Godeps/_workspace
|
||||
|
||||
Files: *
|
||||
Copyright: 2014 cryptix
|
||||
Copyright: 2018 idk
|
||||
License: GPL-2.0
|
||||
|
||||
Files: debug
|
||||
Copyright: 2014 cryptix
|
||||
LICENSE: MIT
|
||||
|
||||
Files: debian/*
|
||||
Copyright: 2019 idk <hankhill19580@gmail.com>
|
||||
License: GPL-2.0
|
||||
Comment: Debian packaging is licensed under the same terms as upstream
|
||||
|
||||
License: GPL-2.0
|
||||
TODO
|
2
debian/gbp.conf
vendored
Normal file
2
debian/gbp.conf
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
[DEFAULT]
|
||||
pristine-tar = False
|
4
debian/rules
vendored
Executable file
4
debian/rules
vendored
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
%:
|
||||
dh $@ --buildsystem=golang --with=golang
|
1
debian/source/format
vendored
Normal file
1
debian/source/format
vendored
Normal file
@@ -0,0 +1 @@
|
||||
3.0 (native)
|
4
debian/watch
vendored
Normal file
4
debian/watch
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
version=4
|
||||
opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/golang-github-eyedeekay-gosam-\$1\.tar\.gz/,\
|
||||
uversionmangle=s/(\d)[_\.\-\+]?(RC|rc|pre|dev|beta|alpha)[.]?(\d*)$/\$1~\$2\$3/ \
|
||||
https://github.com/eyedeekay/gosam/tags .*/v?(\d\S*)\.tar\.gz
|
84
dial.go
84
dial.go
@@ -1,35 +1,91 @@
|
||||
package goSam
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Dial implements the net.Dial function and can be used for http.Transport
|
||||
// DialContext implements the net.DialContext function and can be used for http.Transport
|
||||
func (c *Client) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
errCh := make(chan error, 1)
|
||||
connCh := make(chan net.Conn, 1)
|
||||
go func() {
|
||||
if conn, err := c.DialContextFree(network, addr); err != nil {
|
||||
errCh <- err
|
||||
} else if ctx.Err() != nil {
|
||||
log.Println(ctx)
|
||||
errCh <- ctx.Err()
|
||||
} else {
|
||||
connCh <- conn
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case err := <-errCh:
|
||||
return nil, err
|
||||
case conn := <-connCh:
|
||||
return conn, nil
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (c *Client) Dial(network, addr string) (net.Conn, error) {
|
||||
return c.DialContext(context.TODO(), network, addr)
|
||||
}
|
||||
|
||||
// Dial implements the net.Dial function and can be used for http.Transport
|
||||
func (c *Client) DialContextFree(network, addr string) (net.Conn, error) {
|
||||
if network == "tcp" || network == "tcp6" || network == "tcp4" {
|
||||
return c.DialStreamingContextFree(addr)
|
||||
}
|
||||
if network == "udp" || network == "udp6" || network == "udp4" {
|
||||
return c.DialDatagramContextFree(addr)
|
||||
}
|
||||
if network == "raw" || network == "ip" {
|
||||
return c.DialDatagramContextFree(addr)
|
||||
}
|
||||
return c.DialStreamingContextFree(addr)
|
||||
}
|
||||
|
||||
// DialDatagramContextFree is a "Dialer" for "Client-Like" Datagram connections.
|
||||
// It is also not finished. If you need datagram support right now, use sam3.
|
||||
func (c *Client) DialDatagramContextFree(addr string) (DatagramConn, error) {
|
||||
return nil, fmt.Errorf("Datagram support is not finished yet, come back later`")
|
||||
}
|
||||
|
||||
func (c *Client) DialStreamingContextFree(addr string) (net.Conn, error) {
|
||||
portIdx := strings.Index(addr, ":")
|
||||
if portIdx >= 0 {
|
||||
addr = addr[:portIdx]
|
||||
}
|
||||
addr, err := c.Lookup(addr)
|
||||
if err != nil {
|
||||
log.Printf("LOOKUP DIALER ERROR %s %s", addr, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, _, err := c.CreateStreamSession("")
|
||||
if c.destination == "" {
|
||||
c.destination, err = c.CreateStreamSession(c.destination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if c.d == nil {
|
||||
c.d, err = c.NewClient(c.NewID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
err = c.d.StreamConnect(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newC, err := NewClient(c.samaddr())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = newC.StreamConnect(id, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newC.SamConn, nil
|
||||
return c.d.SamConn, nil
|
||||
}
|
||||
|
@@ -1,53 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/cryptix/goSam"
|
||||
)
|
||||
|
||||
func main() {
|
||||
//In order to enable debugging, pass the SetDebug(true) option.
|
||||
//sam, err := goSam.NewClientFromOptions(SetDebug(true))
|
||||
|
||||
// create a default sam client
|
||||
sam, err := goSam.NewDefaultClient()
|
||||
checkErr(err)
|
||||
|
||||
log.Println("Client Created")
|
||||
|
||||
// create a transport that uses SAM to dial TCP Connections
|
||||
tr := &http.Transport{
|
||||
Dial: sam.Dial,
|
||||
}
|
||||
|
||||
// create a client using this transport
|
||||
client := &http.Client{Transport: tr}
|
||||
|
||||
// send a get request
|
||||
resp, err := client.Get("http://stats.i2p/")
|
||||
checkErr(err)
|
||||
defer resp.Body.Close()
|
||||
|
||||
log.Printf("Get returned %+v\n", resp)
|
||||
|
||||
// create a file for the response
|
||||
file, err := os.Create("stats.html")
|
||||
checkErr(err)
|
||||
defer file.Close()
|
||||
|
||||
// copy the response to the file
|
||||
_, err = io.Copy(file, resp.Body)
|
||||
checkErr(err)
|
||||
|
||||
log.Println("Done.")
|
||||
}
|
||||
|
||||
func checkErr(err error) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
17
go.mod
17
go.mod
@@ -1,6 +1,17 @@
|
||||
module github.com/cryptix/goSam
|
||||
module github.com/eyedeekay/goSam
|
||||
|
||||
require (
|
||||
github.com/cryptix/go v1.3.1
|
||||
github.com/miolini/datacounter v0.0.0-20171104152933-fd4e42a1d5e0 // indirect
|
||||
github.com/eyedeekay/sam3 v0.32.32
|
||||
github.com/getlantern/go-socks5 v0.0.0-20171114193258-79d4dd3e2db5
|
||||
github.com/getlantern/golog v0.0.0-20201105130739-9586b8bde3a9 // indirect
|
||||
github.com/getlantern/netx v0.0.0-20190110220209-9912de6f94fd // indirect
|
||||
github.com/getlantern/ops v0.0.0-20200403153110-8476b16edcd6 // indirect
|
||||
)
|
||||
|
||||
//replace github.com/eyedeekay/gosam v0.1.1-0.20190814195658-27e786578944 => github.com/eyedeekay/goSam ./
|
||||
|
||||
replace github.com/eyedeekay/gosam v0.32.1 => ./
|
||||
|
||||
replace github.com/eyedeekay/goSam v0.32.1 => ./
|
||||
|
||||
go 1.13
|
||||
|
43
go.sum
43
go.sum
@@ -1,8 +1,35 @@
|
||||
github.com/cryptix/go v1.3.1 h1:I9opbROgEpldI0PwkMku0UY2DLFYgelZd9u0uaxmMgY=
|
||||
github.com/cryptix/go v1.3.1/go.mod h1:mFQotm9rTzptzvNjJM+1vSIDa/rVOVqMu0889GIXg70=
|
||||
github.com/go-kit/kit v0.6.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-stack/stack v1.7.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/miolini/datacounter v0.0.0-20171104152933-fd4e42a1d5e0 h1:clkDYGefEWUCwyCrwYn900sOaVGDpinPJgD0W6ebEjs=
|
||||
github.com/miolini/datacounter v0.0.0-20171104152933-fd4e42a1d5e0/go.mod h1:P6fDJzlxN+cWYR09KbE9/ta+Y6JofX9tAUhJpWkWPaM=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/eyedeekay/ramp v0.0.0-20190429201811-305b382042ab h1:EfTRHxGSbiaEyxNzvKRBWVIDw3mD8xXGxj4gvwFzY7Q=
|
||||
github.com/eyedeekay/ramp v0.0.0-20190429201811-305b382042ab/go.mod h1:h7mvUAMgZ/rtRDUOkvKTK+8LnDMeUhJSoa5EPdB51fc=
|
||||
github.com/eyedeekay/sam3 v0.32.2 h1:xODDY5nBVg0oK7KaYk7ofkXFoHPsmI1umhSv1TZlS7s=
|
||||
github.com/eyedeekay/sam3 v0.32.2/go.mod h1:Y3igFVzN4ybqkkpfUWULGhw7WRp8lieq0ORXbLBbcZM=
|
||||
github.com/eyedeekay/sam3 v0.32.31 h1:0fdDAupEQZSETHcyVQAsnFgpYArGJzU+lC2qN6f0GDk=
|
||||
github.com/eyedeekay/sam3 v0.32.32-0.20201122050855-f464873c9350 h1:8R4zcaWsgANiZ4MKKBPUf9Isct2M1IFVUVZdAMqPCmU=
|
||||
github.com/eyedeekay/sam3 v0.32.32-0.20201122050855-f464873c9350/go.mod h1:qRA9KIIVxbrHlkj+ZB+OoxFGFgdKeGp1vSgPw26eOVU=
|
||||
github.com/eyedeekay/sam3 v0.32.32 h1:9Ea1Ere5O8Clx8zYxKnvhrWy7R96Q4FvxlPskYf8VW0=
|
||||
github.com/eyedeekay/sam3 v0.32.32/go.mod h1:qRA9KIIVxbrHlkj+ZB+OoxFGFgdKeGp1vSgPw26eOVU=
|
||||
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 h1:NRUJuo3v3WGC/g5YiyF790gut6oQr5f3FBI88Wv0dx4=
|
||||
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY=
|
||||
github.com/getlantern/errors v1.0.1 h1:XukU2whlh7OdpxnkXhNH9VTLVz0EVPGKDV5K0oWhvzw=
|
||||
github.com/getlantern/errors v1.0.1/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A=
|
||||
github.com/getlantern/go-socks5 v0.0.0-20171114193258-79d4dd3e2db5 h1:RBKofGGMt2k6eGBwX8mky9qunjL+KnAp9JdzXjiRkRw=
|
||||
github.com/getlantern/go-socks5 v0.0.0-20171114193258-79d4dd3e2db5/go.mod h1:kGHRXch95rnGLHjER/GhhFiHvfnqNz7KqWD9kGfATHY=
|
||||
github.com/getlantern/golog v0.0.0-20201105130739-9586b8bde3a9 h1:8MYJU90rB1bsavemKSAuDKBjtAKo5xq95bEPOnzV7CE=
|
||||
github.com/getlantern/golog v0.0.0-20201105130739-9586b8bde3a9/go.mod h1:ZyIjgH/1wTCl+B+7yH1DqrWp6MPJqESmwmEQ89ZfhvA=
|
||||
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 h1:micT5vkcr9tOVk1FiH8SWKID8ultN44Z+yzd2y/Vyb0=
|
||||
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7/go.mod h1:dD3CgOrwlzca8ed61CsZouQS5h5jIzkK9ZWrTcf0s+o=
|
||||
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 h1:XYzSdCbkzOC0FDNrgJqGRo8PCMFOBFL9py72DRs7bmc=
|
||||
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2kW1TOOrVy+r41Za2MxXM+hhqTtY3oBKd2AgFA=
|
||||
github.com/getlantern/netx v0.0.0-20190110220209-9912de6f94fd h1:mn98vs69Kqw56iKhR82mjk16Q1q5aDFFW0E89/QbXkQ=
|
||||
github.com/getlantern/netx v0.0.0-20190110220209-9912de6f94fd/go.mod h1:wKdY0ikOgzrWSeB9UyBVKPRhjXQ+vTb+BPeJuypUuNE=
|
||||
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f h1:wrYrQttPS8FHIRSlsrcuKazukx/xqO/PpLZzZXsF+EA=
|
||||
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
|
||||
github.com/getlantern/ops v0.0.0-20200403153110-8476b16edcd6 h1:QthAQCekS1YOeYWSvoHI6ZatlG4B+GBDLxV/2ZkBsTA=
|
||||
github.com/getlantern/ops v0.0.0-20200403153110-8476b16edcd6/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
|
32
i2pkeys.go
Normal file
32
i2pkeys.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package goSam
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// NewDestination generates a new I2P destination, creating the underlying
|
||||
// public/private keys in the process. The public key can be used to send messages
|
||||
// to the destination, while the private key can be used to reply to messages
|
||||
func (c *Client) NewDestination(sigType ...string) (string, string, error) {
|
||||
var (
|
||||
sigtmp string
|
||||
)
|
||||
if len(sigType) > 0 {
|
||||
sigtmp = sigType[0]
|
||||
}
|
||||
r, err := c.sendCmd(
|
||||
"DEST GENERATE %s\n",
|
||||
sigtmp,
|
||||
)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
var pub, priv string
|
||||
if priv = r.Pairs["PRIV"]; priv == "" {
|
||||
return "", "", errors.New("failed to generate private destination key")
|
||||
}
|
||||
if pub = r.Pairs["PUB"]; pub == "" {
|
||||
return priv, "", errors.New("failed to generate public destination key")
|
||||
}
|
||||
return priv, pub, nil
|
||||
}
|
43
naming.go
43
naming.go
@@ -1,7 +1,10 @@
|
||||
package goSam
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
@@ -14,7 +17,7 @@ func (c *Client) Lookup(name string) (string, error) {
|
||||
|
||||
// TODO: move check into sendCmd()
|
||||
if r.Topic != "NAMING" || r.Type != "REPLY" {
|
||||
return "", fmt.Errorf("Unknown Reply: %+v\n", r)
|
||||
return "", fmt.Errorf("Naming Unknown Reply: %s, %s\n", r.Topic, r.Type)
|
||||
}
|
||||
|
||||
result := r.Pairs["RESULT"]
|
||||
@@ -32,3 +35,41 @@ func (c *Client) Lookup(name string) (string, error) {
|
||||
|
||||
return r.Pairs["VALUE"], nil
|
||||
}
|
||||
|
||||
func (c *Client) forward(client, conn net.Conn) {
|
||||
defer client.Close()
|
||||
defer conn.Close()
|
||||
go func() {
|
||||
// defer client.Close()
|
||||
// defer conn.Close()
|
||||
io.Copy(client, conn)
|
||||
}()
|
||||
go func() {
|
||||
// defer client.Close()
|
||||
// defer conn.Close()
|
||||
io.Copy(conn, client)
|
||||
}()
|
||||
}
|
||||
|
||||
func (c *Client) Resolve(ctx context.Context, name string) (context.Context, net.IP, error) {
|
||||
// if c.lastaddr == "invalid" || c.lastaddr != name {
|
||||
client, err := c.DialContext(ctx, "", name)
|
||||
if err != nil {
|
||||
return ctx, nil, err
|
||||
}
|
||||
ln, err := net.Listen("tcp", "127.0.0.1:")
|
||||
if err != nil {
|
||||
return ctx, nil, err
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
go c.forward(client, conn)
|
||||
}
|
||||
}()
|
||||
// }
|
||||
return ctx, nil, nil
|
||||
}
|
||||
|
@@ -1,3 +1,6 @@
|
||||
//go:build nettest
|
||||
// +build nettest
|
||||
|
||||
package goSam
|
||||
|
||||
import (
|
||||
@@ -8,8 +11,10 @@ import (
|
||||
func TestClientLookupInvalid(t *testing.T) {
|
||||
var err error
|
||||
|
||||
setup(t)
|
||||
defer teardown(t)
|
||||
client, err := NewClientFromOptions(SetDebug(true))
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
}
|
||||
|
||||
addr, err := client.Lookup(`!(@#)`)
|
||||
if addr != "" || err == nil {
|
||||
@@ -20,12 +25,15 @@ func TestClientLookupInvalid(t *testing.T) {
|
||||
if !ok {
|
||||
t.Fatalf("client.Lookup() should return a ReplyError")
|
||||
}
|
||||
if repErr.Result != ResultInvalidKey {
|
||||
t.Errorf("client.Lookup() should throw an ResultKeyNotFound error.\nGot:%+v\n", repErr)
|
||||
if repErr.Result != ResultKeyNotFound {
|
||||
t.Errorf("client.Lookup() should throw an ResultKeyNotFound error.\nGot:%+v%s%s\n", repErr, "!=", ResultKeyNotFound)
|
||||
}
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleClient_Lookup() {
|
||||
func TestClientLookupValid(t *testing.T) {
|
||||
client, err := NewDefaultClient()
|
||||
if err != nil {
|
||||
fmt.Printf("NewDefaultClient() should not throw an error.\n%s\n", err)
|
||||
@@ -38,11 +46,17 @@ func ExampleClient_Lookup() {
|
||||
return
|
||||
}
|
||||
|
||||
if addr == `GKapJ8koUcBj~jmQzHsTYxDg2tpfWj0xjQTzd8BhfC9c3OS5fwPBNajgF-eOD6eCjFTqTlorlh7Hnd8kXj1qblUGXT-tDoR9~YV8dmXl51cJn9MVTRrEqRWSJVXbUUz9t5Po6Xa247Vr0sJn27R4KoKP8QVj1GuH6dB3b6wTPbOamC3dkO18vkQkfZWUdRMDXk0d8AdjB0E0864nOT~J9Fpnd2pQE5uoFT6P0DqtQR2jsFvf9ME61aqLvKPPWpkgdn4z6Zkm-NJOcDz2Nv8Si7hli94E9SghMYRsdjU-knObKvxiagn84FIwcOpepxuG~kFXdD5NfsH0v6Uri3usE3XWD7Pw6P8qVYF39jUIq4OiNMwPnNYzy2N4mDMQdsdHO3LUVh~DEppOy9AAmEoHDjjJxt2BFBbGxfdpZCpENkwvmZeYUyNCCzASqTOOlNzdpne8cuesn3NDXIpNnqEE6Oe5Qm5YOJykrX~Vx~cFFT3QzDGkIjjxlFBsjUJyYkFjBQAEAAcAAA==` {
|
||||
t.Log("Success")
|
||||
} else {
|
||||
t.Errorf("Address of zzz.i2p != \nGKapJ8koUcBj~jmQzHsTYxDg2tpfWj0xjQTzd8BhfC9c3OS5fwPBNajgF-eOD6eCjFTqTlorlh7Hnd8kXj1qblUGXT-tDoR9~YV8dmXl51cJn9MVTRrEqRWSJVXbUUz9t5Po6Xa247Vr0sJn27R4KoKP8QVj1GuH6dB3b6wTPbOamC3dkO18vkQkfZWUdRMDXk0d8AdjB0E0864nOT~J9Fpnd2pQE5uoFT6P0DqtQR2jsFvf9ME61aqLvKPPWpkgdn4z6Zkm-NJOcDz2Nv8Si7hli94E9SghMYRsdjU-knObKvxiagn84FIwcOpepxuG~kFXdD5NfsH0v6Uri3usE3XWD7Pw6P8qVYF39jUIq4OiNMwPnNYzy2N4mDMQdsdHO3LUVh~DEppOy9AAmEoHDjjJxt2BFBbGxfdpZCpENkwvmZeYUyNCCzASqTOOlNzdpne8cuesn3NDXIpNnqEE6Oe5Qm5YOJykrX~Vx~cFFT3QzDGkIjjxlFBsjUJyYkFjBQAEAAcAAA==\n, check to see if it changed, %s", addr)
|
||||
}
|
||||
|
||||
fmt.Println("Address of zzz.i2p:")
|
||||
// Addresses change all the time
|
||||
fmt.Println(addr)
|
||||
|
||||
// Output:
|
||||
//Address of zzz.i2p:
|
||||
//GKapJ8koUcBj~jmQzHsTYxDg2tpfWj0xjQTzd8BhfC9c3OS5fwPBNajgF-eOD6eCjFTqTlorlh7Hnd8kXj1qblUGXT-tDoR9~YV8dmXl51cJn9MVTRrEqRWSJVXbUUz9t5Po6Xa247Vr0sJn27R4KoKP8QVj1GuH6dB3b6wTPbOamC3dkO18vkQkfZWUdRMDXk0d8AdjB0E0864nOT~J9Fpnd2pQE5uoFT6P0DqtQR2jsFvf9ME61aqLvKPPWpkgdn4z6Zkm-NJOcDz2Nv8Si7hli94E9SghMYRsdjU-knObKvxiagn84FIwcOpepxuG~kFXdD5NfsH0v6Uri3usE3uSzpWS0EHmrlfoLr5uGGd9ZHwwCIcgfOATaPRMUEQxiK9q48PS0V3EXXO4-YLT0vIfk4xO~XqZpn8~PW1kFe2mQMHd7oO89yCk-3yizRG3UyFtI7-mO~eCI6-m1spYoigStgoupnC3G85gJkqEjMm49gUjbhfWKWI-6NwTj0ZnAAAA
|
||||
//
|
||||
}
|
||||
|
293
options.go
293
options.go
@@ -62,6 +62,48 @@ func SetHost(s string) func(*Client) error {
|
||||
}
|
||||
}
|
||||
|
||||
func SetSAMMinVersion(i int) func(*Client) error {
|
||||
return func(c *Client) error {
|
||||
if i < 0 {
|
||||
return fmt.Errorf("SAM version must be greater than or equal to 0")
|
||||
}
|
||||
if i > 3 {
|
||||
return fmt.Errorf("SAM version must be less than or equal to 3")
|
||||
}
|
||||
c.sammin = i
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func SetSAMMaxVersion(i int) func(*Client) error {
|
||||
return func(c *Client) error {
|
||||
if i < 0 {
|
||||
return fmt.Errorf("SAM version must be greater than or equal to 0")
|
||||
}
|
||||
if i > 3 {
|
||||
return fmt.Errorf("SAM version must be less than or equal to 3")
|
||||
}
|
||||
c.sammin = i
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//SetLocalDestination sets the local destination of the tunnel from a private
|
||||
//key
|
||||
func SetLocalDestination(s string) func(*Client) error {
|
||||
return func(c *Client) error {
|
||||
c.destination = s
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func setid(s int32) func(*Client) error {
|
||||
return func(c *Client) error {
|
||||
c.id = s
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//SetPort sets the port of the client's SAM bridge using a string
|
||||
func SetPort(s string) func(*Client) error {
|
||||
return func(c *Client) error {
|
||||
@@ -88,6 +130,58 @@ func SetPortInt(i int) func(*Client) error {
|
||||
}
|
||||
}
|
||||
|
||||
//SetFromPort sets the port of the client's SAM bridge using a string
|
||||
func SetFromPort(s string) func(*Client) error {
|
||||
return func(c *Client) error {
|
||||
port, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Invalid port; non-number")
|
||||
}
|
||||
if port < 65536 && port > -1 {
|
||||
c.fromport = s
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Invalid port")
|
||||
}
|
||||
}
|
||||
|
||||
//SetFromPortInt sets the port of the client's SAM bridge using a string
|
||||
func SetFromPortInt(i int) func(*Client) error {
|
||||
return func(c *Client) error {
|
||||
if i < 65536 && i > -1 {
|
||||
c.fromport = strconv.Itoa(i)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Invalid port")
|
||||
}
|
||||
}
|
||||
|
||||
//SetToPort sets the port of the client's SAM bridge using a string
|
||||
func SetToPort(s string) func(*Client) error {
|
||||
return func(c *Client) error {
|
||||
port, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Invalid port; non-number")
|
||||
}
|
||||
if port < 65536 && port > -1 {
|
||||
c.toport = s
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Invalid port")
|
||||
}
|
||||
}
|
||||
|
||||
//SetToPortInt sets the port of the client's SAM bridge using a string
|
||||
func SetToPortInt(i int) func(*Client) error {
|
||||
return func(c *Client) error {
|
||||
if i < 65536 && i > -1 {
|
||||
c.fromport = strconv.Itoa(i)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Invalid port")
|
||||
}
|
||||
}
|
||||
|
||||
//SetDebug enables debugging messages
|
||||
func SetDebug(b bool) func(*Client) error {
|
||||
return func(c *Client) error {
|
||||
@@ -200,7 +294,16 @@ func SetEncrypt(b bool) func(*Client) error {
|
||||
}
|
||||
}
|
||||
|
||||
//SetReduceIdle tells the router to use an encrypted leaseset
|
||||
//SetLeaseSetEncType tells the router to use an encrypted leaseset of a specific type.
|
||||
//defaults to 4,0
|
||||
func SetLeaseSetEncType(b string) func(*Client) error {
|
||||
return func(c *Client) error {
|
||||
c.leaseSetEncType = b
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
//SetReduceIdle sets the created tunnels to be reduced during extended idle time to avoid excessive resource usage
|
||||
func SetReduceIdle(b bool) func(*Client) error {
|
||||
return func(c *Client) error {
|
||||
c.reduceIdle = b
|
||||
@@ -208,10 +311,10 @@ func SetReduceIdle(b bool) func(*Client) error {
|
||||
}
|
||||
}
|
||||
|
||||
//SetReduceIdleTime sets the inbound tunnel backups
|
||||
//SetReduceIdleTime sets time to wait before the tunnel quantity is reduced
|
||||
func SetReduceIdleTime(u uint) func(*Client) error {
|
||||
return func(c *Client) error {
|
||||
if u > 300000 {
|
||||
if u > 299999 {
|
||||
c.reduceIdleTime = u
|
||||
return nil
|
||||
}
|
||||
@@ -219,7 +322,7 @@ func SetReduceIdleTime(u uint) func(*Client) error {
|
||||
}
|
||||
}
|
||||
|
||||
//SetReduceIdleQuantity sets the inbound tunnel backups
|
||||
//SetReduceIdleQuantity sets number of tunnels to keep alive during an extended idle period
|
||||
func SetReduceIdleQuantity(u uint) func(*Client) error {
|
||||
return func(c *Client) error {
|
||||
if u < 5 {
|
||||
@@ -230,7 +333,7 @@ func SetReduceIdleQuantity(u uint) func(*Client) error {
|
||||
}
|
||||
}
|
||||
|
||||
//SetCloseIdle tells the router to use an encrypted leaseset
|
||||
//SetCloseIdle sets the tunnels to close after a specific amount of time
|
||||
func SetCloseIdle(b bool) func(*Client) error {
|
||||
return func(c *Client) error {
|
||||
c.closeIdle = b
|
||||
@@ -238,10 +341,10 @@ func SetCloseIdle(b bool) func(*Client) error {
|
||||
}
|
||||
}
|
||||
|
||||
//SetCloseIdleTime sets the inbound tunnel backups
|
||||
//SetCloseIdleTime sets the time in milliseconds to wait before closing tunnels
|
||||
func SetCloseIdleTime(u uint) func(*Client) error {
|
||||
return func(c *Client) error {
|
||||
if u > 300000 {
|
||||
if u > 299999 {
|
||||
c.closeIdleTime = u
|
||||
return nil
|
||||
}
|
||||
@@ -249,103 +352,205 @@ func SetCloseIdleTime(u uint) func(*Client) error {
|
||||
}
|
||||
}
|
||||
|
||||
//SetCompression sets the tunnels to close after a specific amount of time
|
||||
func SetCompression(b bool) func(*Client) error {
|
||||
return func(c *Client) error {
|
||||
c.compression = b
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/* SAM v 3.1 Options*/
|
||||
|
||||
//SetSignatureType tells gosam to pass SAM a signature_type parameter with one
|
||||
// of the following values:
|
||||
// "SIGNATURE_TYPE=DSA_SHA1",
|
||||
// "SIGNATURE_TYPE=ECDSA_SHA256_P256",
|
||||
// "SIGNATURE_TYPE=ECDSA_SHA384_P384",
|
||||
// "SIGNATURE_TYPE=ECDSA_SHA512_P521",
|
||||
// "SIGNATURE_TYPE=EdDSA_SHA512_Ed25519",
|
||||
// or an empty string
|
||||
func SetSignatureType(s string) func(*Client) error {
|
||||
return func(c *Client) error {
|
||||
if s == "" {
|
||||
c.sigType = ""
|
||||
return nil
|
||||
}
|
||||
for _, valid := range SAMsigTypes {
|
||||
if s == valid {
|
||||
c.sigType = valid
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("Invalid signature type specified at construction time")
|
||||
}
|
||||
}
|
||||
|
||||
//return the from port as a string.
|
||||
func (c *Client) from() string {
|
||||
if c.fromport == "FROM_PORT=0" {
|
||||
return ""
|
||||
}
|
||||
if c.fromport == "0" {
|
||||
return ""
|
||||
}
|
||||
if c.fromport == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf(" FROM_PORT=%v ", c.fromport)
|
||||
}
|
||||
|
||||
//return the to port as a string.
|
||||
func (c *Client) to() string {
|
||||
if c.fromport == "TO_PORT=0" {
|
||||
return ""
|
||||
}
|
||||
if c.fromport == "0" {
|
||||
return ""
|
||||
}
|
||||
if c.toport == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf(" TO_PORT=%v ", c.toport)
|
||||
}
|
||||
|
||||
//return the signature type as a string.
|
||||
func (c *Client) sigtype() string {
|
||||
return fmt.Sprintf(" %s ", c.sigType)
|
||||
}
|
||||
|
||||
//return the inbound length as a string.
|
||||
func (c *Client) inlength() string {
|
||||
return fmt.Sprintf("inbound.length=%d", c.inLength)
|
||||
return fmt.Sprintf(" inbound.length=%d ", c.inLength)
|
||||
}
|
||||
|
||||
//return the outbound length as a string.
|
||||
func (c *Client) outlength() string {
|
||||
return fmt.Sprintf("outbound.length=%d", c.outLength)
|
||||
return fmt.Sprintf(" outbound.length=%d ", c.outLength)
|
||||
}
|
||||
|
||||
//return the inbound length variance as a string.
|
||||
func (c *Client) invariance() string {
|
||||
return fmt.Sprintf("inbound.lengthVariance=%d", c.inVariance)
|
||||
return fmt.Sprintf(" inbound.lengthVariance=%d ", c.inVariance)
|
||||
}
|
||||
|
||||
//return the outbound length variance as a string.
|
||||
func (c *Client) outvariance() string {
|
||||
return fmt.Sprintf("outbound.lengthVariance=%d", c.outVariance)
|
||||
return fmt.Sprintf(" outbound.lengthVariance=%d ", c.outVariance)
|
||||
}
|
||||
|
||||
//return the inbound tunnel quantity as a string.
|
||||
func (c *Client) inquantity() string {
|
||||
return fmt.Sprintf("inbound.quantity=%d", c.inQuantity)
|
||||
return fmt.Sprintf(" inbound.quantity=%d ", c.inQuantity)
|
||||
}
|
||||
|
||||
//return the outbound tunnel quantity as a string.
|
||||
func (c *Client) outquantity() string {
|
||||
return fmt.Sprintf("outbound.quantity=%d", c.outQuantity)
|
||||
return fmt.Sprintf(" outbound.quantity=%d ", c.outQuantity)
|
||||
}
|
||||
|
||||
//return the inbound tunnel quantity as a string.
|
||||
func (c *Client) inbackups() string {
|
||||
return fmt.Sprintf("inbound.backupQuantity=%d", c.inQuantity)
|
||||
return fmt.Sprintf(" inbound.backupQuantity=%d ", c.inQuantity)
|
||||
}
|
||||
|
||||
//return the outbound tunnel quantity as a string.
|
||||
func (c *Client) outbackups() string {
|
||||
return fmt.Sprintf("outbound.backupQuantity=%d", c.outQuantity)
|
||||
return fmt.Sprintf(" outbound.backupQuantity=%d ", c.outQuantity)
|
||||
}
|
||||
|
||||
func (c *Client) encryptlease() string {
|
||||
if c.encryptLease {
|
||||
return "i2cp.encryptLeaseSet=true"
|
||||
return " i2cp.encryptLeaseSet=true "
|
||||
}
|
||||
return "i2cp.encryptLeaseSet=false"
|
||||
return " i2cp.encryptLeaseSet=false "
|
||||
}
|
||||
|
||||
func (c *Client) leasesetenctype() string {
|
||||
if c.encryptLease {
|
||||
return fmt.Sprintf(" i2cp.leaseSetEncType=%s ", c.leaseSetEncType)
|
||||
}
|
||||
return " i2cp.leaseSetEncType=4,0 "
|
||||
}
|
||||
|
||||
func (c *Client) dontpublishlease() string {
|
||||
if c.dontPublishLease {
|
||||
return "i2cp.dontPublishLeaseSet=true"
|
||||
return " i2cp.dontPublishLeaseSet=true "
|
||||
}
|
||||
return "i2cp.dontPublishLeaseSet=false"
|
||||
return " i2cp.dontPublishLeaseSet=false "
|
||||
}
|
||||
|
||||
func (c *Client) closeonidle() string {
|
||||
if c.closeIdle {
|
||||
return "i2cp.closeOnIdle=true"
|
||||
return " i2cp.closeOnIdle=true "
|
||||
}
|
||||
return "i2cp.closeOnIdle=false"
|
||||
return " i2cp.closeOnIdle=false "
|
||||
}
|
||||
|
||||
func (c *Client) closeidletime() string {
|
||||
return fmt.Sprintf("i2cp.closeIdleTime=%d", c.closeIdleTime)
|
||||
return fmt.Sprintf(" i2cp.closeIdleTime=%d ", c.closeIdleTime)
|
||||
}
|
||||
|
||||
func (c *Client) reduceonidle() string {
|
||||
if c.reduceIdle {
|
||||
return "i2cp.reduceOnIdle=true"
|
||||
return " i2cp.reduceOnIdle=true "
|
||||
}
|
||||
return "i2cp.reduceOnIdle=false"
|
||||
return " i2cp.reduceOnIdle=false "
|
||||
}
|
||||
|
||||
func (c *Client) reduceidletime() string {
|
||||
return fmt.Sprintf("i2cp.reduceIdleTime=%d", c.reduceIdleTime)
|
||||
return fmt.Sprintf(" i2cp.reduceIdleTime=%d ", c.reduceIdleTime)
|
||||
}
|
||||
|
||||
func (c *Client) reduceidlecount() string {
|
||||
return fmt.Sprintf("i2cp.reduceIdleQuantity=%d", c.reduceIdleQuantity)
|
||||
return fmt.Sprintf(" i2cp.reduceIdleQuantity=%d ", c.reduceIdleQuantity)
|
||||
}
|
||||
|
||||
//return all options as string array ready for passing to sendcmd
|
||||
func (c *Client) allOptions() []string {
|
||||
return []string{
|
||||
c.inlength(),
|
||||
c.outlength(),
|
||||
c.invariance(),
|
||||
c.outvariance(),
|
||||
c.inquantity(),
|
||||
c.outquantity(),
|
||||
c.inbackups(),
|
||||
c.outbackups(),
|
||||
c.dontpublishlease(),
|
||||
c.encryptlease(),
|
||||
c.reduceonidle(),
|
||||
c.reduceidletime(),
|
||||
c.reduceidlecount(),
|
||||
c.closeonidle(),
|
||||
c.closeidletime(),
|
||||
func (c *Client) compresion() string {
|
||||
if c.compression {
|
||||
return " i2cp.gzip=true "
|
||||
}
|
||||
return " i2cp.gzip=false "
|
||||
}
|
||||
|
||||
//return all options as string ready for passing to sendcmd
|
||||
func (c *Client) allOptions() string {
|
||||
return c.inlength() +
|
||||
c.outlength() +
|
||||
c.invariance() +
|
||||
c.outvariance() +
|
||||
c.inquantity() +
|
||||
c.outquantity() +
|
||||
c.inbackups() +
|
||||
c.outbackups() +
|
||||
c.dontpublishlease() +
|
||||
c.encryptlease() +
|
||||
c.leasesetenctype() +
|
||||
c.reduceonidle() +
|
||||
c.reduceidletime() +
|
||||
c.reduceidlecount() +
|
||||
c.closeonidle() +
|
||||
c.closeidletime() +
|
||||
c.compresion()
|
||||
}
|
||||
|
||||
//Print return all options as string
|
||||
func (c *Client) Print() string {
|
||||
return c.inlength() +
|
||||
c.outlength() +
|
||||
c.invariance() +
|
||||
c.outvariance() +
|
||||
c.inquantity() +
|
||||
c.outquantity() +
|
||||
c.inbackups() +
|
||||
c.outbackups() +
|
||||
c.dontpublishlease() +
|
||||
c.encryptlease() +
|
||||
c.leasesetenctype() +
|
||||
c.reduceonidle() +
|
||||
c.reduceidletime() +
|
||||
c.reduceidlecount() +
|
||||
c.closeonidle() +
|
||||
c.closeidletime() +
|
||||
c.compresion()
|
||||
}
|
||||
|
322
options_test.go
322
options_test.go
@@ -1,241 +1,189 @@
|
||||
//go:build nettest
|
||||
// +build nettest
|
||||
|
||||
package goSam
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
//"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// helper to validate sendCmd inputs
|
||||
func (c *Client) validCmd(str string, args ...interface{}) (string, error) {
|
||||
if s := fmt.Sprintf(str, args...); strings.Contains(s, "\n") {
|
||||
sl := strings.Split(s, "\n")
|
||||
if len(sl) == 2 {
|
||||
if sl[1] != "" {
|
||||
return sl[1], fmt.Errorf("Error, there should be no options after the newline")
|
||||
}
|
||||
for li, in := range sl {
|
||||
fmt.Println(li, in)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
return "", fmt.Errorf("Error, invalid length: %d", len(sl))
|
||||
}
|
||||
return "", fmt.Errorf("Error, invalid input")
|
||||
}
|
||||
|
||||
func (c *Client) validCreate() (string, error) {
|
||||
id := rand.Int31n(math.MaxInt32)
|
||||
result, err := c.validCmd("SESSION CREATE STYLE=STREAM ID=%d DESTINATION=%s %s\n", id, "abc.i2p", c.allOptions())
|
||||
return result, err
|
||||
}
|
||||
|
||||
func TestOptionAddrString(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetAddr("127.0.0.1:7656"), SetDebug(true))
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
t.Fatalf("NewClientFromOptions() Error: %q\n", err)
|
||||
}
|
||||
if result, err := client.validCreate(); err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
} else {
|
||||
t.Log(result)
|
||||
}
|
||||
_, err = client.CreateStreamSession("")
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
/* fmt.Printf("\t destination- %s \n", dest)
|
||||
fmt.Printf("\t address64- %s \t", client.Base64())
|
||||
fmt.Printf("\t address- %s \t", client.Base32())*/
|
||||
}
|
||||
|
||||
func TestOptionAddrStringLh(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetAddr("localhost:7656"), SetDebug(true))
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
t.Fatalf("NewClientFromOptions() Error: %q\n", err)
|
||||
}
|
||||
if result, err := client.validCreate(); err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
} else {
|
||||
t.Log(result)
|
||||
}
|
||||
_, err = client.CreateStreamSession("")
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
/* fmt.Printf("\t destination- %s \n", dest)
|
||||
fmt.Printf("\t address64- %s \t", client.Base64())
|
||||
fmt.Printf("\t address- %s \t", client.Base32())*/
|
||||
}
|
||||
|
||||
func TestOptionAddrSlice(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetAddr("127.0.0.1", "7656"), SetDebug(true))
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
t.Fatalf("NewClientFromOptions() Error: %q\n", err)
|
||||
}
|
||||
if result, err := client.validCreate(); err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
} else {
|
||||
t.Log(result)
|
||||
}
|
||||
_, err = client.CreateStreamSession("")
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
/* fmt.Printf("\t destination- %s \n", dest)
|
||||
fmt.Printf("\t address64- %s \t", client.Base64())
|
||||
fmt.Printf("\t address- %s \t", client.Base32())*/
|
||||
}
|
||||
|
||||
func TestOptionAddrMixedSlice(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetAddrMixed("127.0.0.1", 7656), SetDebug(true))
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
t.Fatalf("NewClientFromOptions() Error: %q\n", err)
|
||||
}
|
||||
if result, err := client.validCreate(); err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
} else {
|
||||
t.Log(result)
|
||||
}
|
||||
_, err = client.CreateStreamSession("")
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
/* fmt.Printf("\t destination- %s \n", dest)
|
||||
fmt.Printf("\t address64- %s \t", client.Base64())
|
||||
fmt.Printf("\t address- %s \t", client.Base32())*/
|
||||
}
|
||||
|
||||
func TestOptionHost(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetHost("127.0.0.1"), SetDebug(true))
|
||||
client, err := NewClientFromOptions(
|
||||
SetHost("127.0.0.1"),
|
||||
SetPort("7656"),
|
||||
SetInLength(3),
|
||||
SetOutLength(3),
|
||||
SetInVariance(1),
|
||||
SetOutVariance(1),
|
||||
SetInQuantity(6),
|
||||
SetOutQuantity(6),
|
||||
SetInBackups(2),
|
||||
SetOutBackups(2),
|
||||
SetEncrypt(true),
|
||||
SetDebug(true),
|
||||
SetUnpublished(true),
|
||||
SetReduceIdle(true),
|
||||
SetReduceIdleTime(300001),
|
||||
SetReduceIdleQuantity(4),
|
||||
SetCloseIdle(true),
|
||||
SetCloseIdleTime(300001),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
}
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptionPort(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetPort("7656"), SetDebug(true))
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
t.Fatalf("NewClientFromOptions() Error: %q\n", err)
|
||||
}
|
||||
if result, err := client.validCreate(); err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
} else {
|
||||
t.Log(result)
|
||||
}
|
||||
_, err = client.CreateStreamSession("")
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
/* fmt.Printf("\t destination- %s \n", dest)
|
||||
fmt.Printf("\t address64- %s \t", client.Base64())
|
||||
fmt.Printf("\t address- %s \t", client.Base32())*/
|
||||
}
|
||||
|
||||
func TestOptionPortInt(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetPortInt(7656), SetDebug(true))
|
||||
client, err := NewClientFromOptions(
|
||||
SetHost("127.0.0.1"),
|
||||
SetPortInt(7656),
|
||||
SetInLength(3),
|
||||
SetOutLength(3),
|
||||
SetInVariance(1),
|
||||
SetOutVariance(1),
|
||||
SetInQuantity(6),
|
||||
SetOutQuantity(6),
|
||||
SetInBackups(2),
|
||||
SetOutBackups(2),
|
||||
SetEncrypt(true),
|
||||
SetDebug(true),
|
||||
SetUnpublished(true),
|
||||
SetReduceIdle(true),
|
||||
SetReduceIdleTime(300001),
|
||||
SetReduceIdleQuantity(4),
|
||||
SetCloseIdle(true),
|
||||
SetCloseIdleTime(300001),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
}
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptionDebug(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetDebug(true))
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
}
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptionInLength(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetInLength(3), SetDebug(true))
|
||||
client.inlength()
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
}
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptionOutLength(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetInLength(3), SetDebug(true))
|
||||
client.outlength()
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
}
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptionInVariance(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetInVariance(1), SetDebug(true))
|
||||
client.invariance()
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
}
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptionOutVariance(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetOutVariance(1), SetDebug(true))
|
||||
client.outvariance()
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
}
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptionInQuantity(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetInQuantity(6), SetDebug(true))
|
||||
client.inquantity()
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
}
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptionOutQuantity(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetOutQuantity(6), SetDebug(true))
|
||||
client.outquantity()
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
}
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptionInBackups(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetInBackups(5), SetDebug(true))
|
||||
client.inbackups()
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
}
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptionOutBackups(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetOutBackups(5), SetDebug(true))
|
||||
client.outbackups()
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
}
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptionEncryptLease(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetEncrypt(true), SetDebug(true))
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
}
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptionUnpublishedLease(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetUnpublished(true), SetDebug(true))
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
}
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptionReduceIdle(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetReduceIdle(true), SetDebug(true))
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
}
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptionReduceIdleTime(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetReduceIdleTime(300001), SetDebug(true))
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
}
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptionReduceIdleCount(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetReduceIdleQuantity(4), SetDebug(true))
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
}
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptionCloseIdle(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetCloseIdle(true), SetDebug(true))
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
}
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptionCloseIdleTime(t *testing.T) {
|
||||
client, err := NewClientFromOptions(SetCloseIdleTime(300001), SetDebug(true))
|
||||
if err != nil {
|
||||
t.Fatalf("NewDefaultClient() Error: %q\n", err)
|
||||
t.Fatalf("NewClientFromOptions() Error: %q\n", err)
|
||||
}
|
||||
if result, err := client.validCreate(); err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
} else {
|
||||
t.Log(result)
|
||||
}
|
||||
_, err = client.CreateStreamSession("")
|
||||
if err := client.Close(); err != nil {
|
||||
t.Fatalf("client.Close() Error: %q\n", err)
|
||||
}
|
||||
/* fmt.Printf("\t destination- %s \n", dest)
|
||||
fmt.Printf("\t address64- %s \t", client.Base64())
|
||||
fmt.Printf("\t address- %s \t", client.Base32())*/
|
||||
}
|
||||
|
@@ -32,6 +32,8 @@ func (r ReplyError) Error() string {
|
||||
type Reply struct {
|
||||
Topic string
|
||||
Type string
|
||||
From string
|
||||
To string
|
||||
|
||||
Pairs map[string]string
|
||||
}
|
||||
@@ -42,6 +44,25 @@ func parseReply(line string) (*Reply, error) {
|
||||
if len(parts) < 3 {
|
||||
return nil, fmt.Errorf("Malformed Reply.\n%s\n", line)
|
||||
}
|
||||
preParseReply := func() []string {
|
||||
val := ""
|
||||
quote := false
|
||||
for _, v := range parts {
|
||||
if strings.Contains(v, "=\"") {
|
||||
quote = true
|
||||
}
|
||||
if strings.Contains(v, "\"\n") || strings.Contains(v, "\" ") {
|
||||
quote = false
|
||||
}
|
||||
if quote {
|
||||
val += v + "_"
|
||||
} else {
|
||||
val += v + " "
|
||||
}
|
||||
}
|
||||
return strings.Split(strings.TrimSuffix(strings.TrimSpace(val), "_"), " ")
|
||||
}
|
||||
parts = preParseReply()
|
||||
|
||||
r := &Reply{
|
||||
Topic: parts[0],
|
||||
@@ -50,14 +71,23 @@ func parseReply(line string) (*Reply, error) {
|
||||
}
|
||||
|
||||
for _, v := range parts[2:] {
|
||||
kvPair := strings.SplitN(v, "=", 2)
|
||||
if kvPair != nil {
|
||||
if len(kvPair) != 2 {
|
||||
return nil, fmt.Errorf("Malformed key-value-pair.\n%s\n", kvPair)
|
||||
if strings.Contains(v, "FROM_PORT") {
|
||||
if v != "FROM_PORT=0" {
|
||||
r.From = v
|
||||
}
|
||||
} else if strings.Contains(v, "TO_PORT") {
|
||||
if v != "TO_PORT=0" {
|
||||
r.To = v
|
||||
}
|
||||
} else {
|
||||
kvPair := strings.SplitN(v, "=", 2)
|
||||
if kvPair != nil {
|
||||
if len(kvPair) != 2 {
|
||||
return nil, fmt.Errorf("Malformed key-value-pair len != 2.\n%s\n", kvPair)
|
||||
}
|
||||
}
|
||||
r.Pairs[kvPair[0]] = kvPair[len(kvPair)-1]
|
||||
}
|
||||
|
||||
r.Pairs[kvPair[0]] = kvPair[1]
|
||||
}
|
||||
|
||||
return r, nil
|
||||
|
101
rw.go
Normal file
101
rw.go
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Henry
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
package goSam
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"log"
|
||||
)
|
||||
|
||||
/*
|
||||
Copy of testing/iotest Read- and WriteLogger, but using %q instead of %x for printing
|
||||
*/
|
||||
|
||||
type writeLogger struct {
|
||||
prefix string
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func (l *writeLogger) Write(p []byte) (n int, err error) {
|
||||
n, err = l.w.Write(p)
|
||||
if err != nil {
|
||||
log.Printf("%s %q: %v", l.prefix, string(p[0:n]), err)
|
||||
} else {
|
||||
log.Printf("%s %q", l.prefix, string(p[0:n]))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewWriteLogger returns a writer that behaves like w except
|
||||
// that it logs (using log.Printf) each write to standard error,
|
||||
// printing the prefix and the hexadecimal data written.
|
||||
func NewWriteLogger(prefix string, w io.Writer) io.Writer {
|
||||
return &writeLogger{prefix, w}
|
||||
}
|
||||
|
||||
type readLogger struct {
|
||||
prefix string
|
||||
r io.Reader
|
||||
}
|
||||
|
||||
func (l *readLogger) Read(p []byte) (n int, err error) {
|
||||
n, err = l.r.Read(p)
|
||||
if err != nil {
|
||||
log.Printf("%s %q: %v", l.prefix, string(p[0:n]), err)
|
||||
} else {
|
||||
log.Printf("%s %q", l.prefix, string(p[0:n]))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewReadLogger returns a reader that behaves like r except
|
||||
// that it logs (using log.Print) each read to standard error,
|
||||
// printing the prefix and the hexadecimal data written.
|
||||
func NewReadLogger(prefix string, r io.Reader) io.Reader {
|
||||
return &readLogger{prefix, r}
|
||||
}
|
||||
|
||||
type readHexLogger struct {
|
||||
prefix string
|
||||
r io.Reader
|
||||
}
|
||||
|
||||
func (l *readHexLogger) Read(p []byte) (n int, err error) {
|
||||
n, err = l.r.Read(p)
|
||||
if err != nil {
|
||||
log.Printf("%s (%d bytes) Error: %v", l.prefix, n, err)
|
||||
} else {
|
||||
log.Printf("%s (%d bytes)", l.prefix, n)
|
||||
}
|
||||
log.Print("\n" + hex.Dump(p[:n]))
|
||||
return
|
||||
}
|
||||
|
||||
// NewReadHexLogger returns a reader that behaves like r except
|
||||
// that it logs to stderr using ecoding/hex.
|
||||
func NewReadHexLogger(prefix string, r io.Reader) io.Reader {
|
||||
return &readHexLogger{prefix, r}
|
||||
}
|
80
rwc.go
Normal file
80
rwc.go
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Henry
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
package goSam
|
||||
|
||||
import (
|
||||
"io"
|
||||
//"github.com/miolini/datacounter"
|
||||
)
|
||||
|
||||
type RWC struct {
|
||||
io.Reader
|
||||
io.Writer
|
||||
c io.Closer
|
||||
}
|
||||
|
||||
func WrapRWC(c io.ReadWriteCloser) io.ReadWriteCloser {
|
||||
rl := NewReadLogger("<", c)
|
||||
wl := NewWriteLogger(">", c)
|
||||
|
||||
return &RWC{
|
||||
Reader: rl,
|
||||
Writer: wl,
|
||||
c: c,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RWC) Close() error {
|
||||
return c.c.Close()
|
||||
}
|
||||
|
||||
/*
|
||||
type Counter struct {
|
||||
io.Reader
|
||||
io.Writer
|
||||
c io.Closer
|
||||
|
||||
Cr *datacounter.ReaderCounter
|
||||
Cw *datacounter.WriterCounter
|
||||
}
|
||||
|
||||
func WrapCounter(c io.ReadWriteCloser) *Counter {
|
||||
rc := datacounter.NewReaderCounter(c)
|
||||
wc := datacounter.NewWriterCounter(c)
|
||||
|
||||
return &Counter{
|
||||
Reader: rc,
|
||||
Writer: wc,
|
||||
c: c,
|
||||
|
||||
Cr: rc,
|
||||
Cw: wc,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Counter) Close() error {
|
||||
return c.c.Close()
|
||||
}
|
||||
*/
|
37
samsocks/main.go
Normal file
37
samsocks/main.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
|
||||
"github.com/eyedeekay/goSam"
|
||||
"github.com/getlantern/go-socks5"
|
||||
"log"
|
||||
)
|
||||
|
||||
var (
|
||||
samaddr = flag.String("sam", "127.0.0.1:7656", "SAM API address to use")
|
||||
socksaddr = flag.String("socks", "127.0.0.1:7675", "SOCKS address to use")
|
||||
)
|
||||
|
||||
func main() {
|
||||
sam, err := goSam.NewClient(*samaddr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
log.Println("Client Created")
|
||||
|
||||
// create a transport that uses SAM to dial TCP Connections
|
||||
conf := &socks5.Config{
|
||||
Dial: sam.DialContext,
|
||||
Resolver: sam,
|
||||
}
|
||||
server, err := socks5.New(conf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Create SOCKS5 proxy on localhost port 8000
|
||||
if err := server.ListenAndServe("tcp", *socksaddr); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
51
sessions.go
51
sessions.go
@@ -2,7 +2,7 @@ package goSam
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
// "math"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
@@ -11,28 +11,55 @@ func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
// CreateStreamSession creates a new STREAM Session.
|
||||
// Returns the Id for the new Client.
|
||||
func (c *Client) CreateStreamSession(dest string) (int32, string, error) {
|
||||
// CreateSession creates a new Session of type style, with an optional destination.
|
||||
// an empty destination is interpreted as "TRANSIENT"
|
||||
// Returns the destination for the new Client or an error.
|
||||
func (c *Client) CreateSession(style, dest string) (string, error) {
|
||||
if dest == "" {
|
||||
dest = "TRANSIENT"
|
||||
}
|
||||
|
||||
id := rand.Int31n(math.MaxInt32)
|
||||
r, err := c.sendCmd("SESSION CREATE STYLE=STREAM ID=%d DESTINATION=%s %s\n", id, dest, c.allOptions())
|
||||
// c.id = id
|
||||
r, err := c.sendCmd(
|
||||
"SESSION CREATE STYLE=%s ID=%s DESTINATION=%s %s %s %s %s \n",
|
||||
style,
|
||||
c.ID(),
|
||||
dest,
|
||||
c.from(),
|
||||
c.to(),
|
||||
c.sigtype(),
|
||||
c.allOptions(),
|
||||
)
|
||||
if err != nil {
|
||||
return -1, "", err
|
||||
return "", err
|
||||
}
|
||||
|
||||
// TODO: move check into sendCmd()
|
||||
if r.Topic != "SESSION" || r.Type != "STATUS" {
|
||||
return -1, "", fmt.Errorf("Unknown Reply: %+v\n", r)
|
||||
return "", fmt.Errorf("Session Unknown Reply: %+v\n", r)
|
||||
}
|
||||
|
||||
result := r.Pairs["RESULT"]
|
||||
if result != "OK" {
|
||||
return -1, "", ReplyError{ResultKeyNotFound, r}
|
||||
return "", ReplyError{ResultKeyNotFound, r}
|
||||
}
|
||||
|
||||
return id, r.Pairs["DESTINATION"], nil
|
||||
c.destination = r.Pairs["DESTINATION"]
|
||||
return c.destination, nil
|
||||
}
|
||||
|
||||
// CreateStreamSession creates a new STREAM Session.
|
||||
// Returns the Id for the new Client.
|
||||
func (c *Client) CreateStreamSession(dest string) (string, error) {
|
||||
return c.CreateSession("STREAM", dest)
|
||||
}
|
||||
|
||||
// CreateDatagramSession creates a new DATAGRAM Session.
|
||||
// Returns the Id for the new Client.
|
||||
func (c *Client) CreateDatagramSession(dest string) (string, error) {
|
||||
return c.CreateSession("DATAGRAM", dest)
|
||||
}
|
||||
|
||||
// CreateRawSession creates a new RAW Session.
|
||||
// Returns the Id for the new Client.
|
||||
func (c *Client) CreateRawSession(dest string) (string, error) {
|
||||
return c.CreateSession("RAW", dest)
|
||||
}
|
||||
|
15
stream.go
15
stream.go
@@ -5,15 +5,18 @@ import (
|
||||
)
|
||||
|
||||
// StreamConnect asks SAM for a TCP-Like connection to dest, has to be called on a new Client
|
||||
func (c *Client) StreamConnect(id int32, dest string) error {
|
||||
r, err := c.sendCmd("STREAM CONNECT ID=%d DESTINATION=%s\n", id, dest)
|
||||
func (c *Client) StreamConnect(dest string) error {
|
||||
if dest == "" {
|
||||
return nil
|
||||
}
|
||||
r, err := c.sendCmd("STREAM CONNECT ID=%s DESTINATION=%s %s %s\n", c.ID(), dest, c.from(), c.to())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: move check into sendCmd()
|
||||
if r.Topic != "STREAM" || r.Type != "STATUS" {
|
||||
return fmt.Errorf("Unknown Reply: %+v\n", r)
|
||||
return fmt.Errorf("Stream Connect Unknown Reply: %+v\n", r)
|
||||
}
|
||||
|
||||
result := r.Pairs["RESULT"]
|
||||
@@ -25,15 +28,15 @@ func (c *Client) StreamConnect(id int32, dest string) error {
|
||||
}
|
||||
|
||||
// StreamAccept asks SAM to accept a TCP-Like connection
|
||||
func (c *Client) StreamAccept(id int32) (*Reply, error) {
|
||||
r, err := c.sendCmd("STREAM ACCEPT ID=%d SILENT=false\n", id)
|
||||
func (c *Client) StreamAccept() (*Reply, error) {
|
||||
r, err := c.sendCmd("STREAM ACCEPT ID=%s SILENT=false\n", c.ID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: move check into sendCmd()
|
||||
if r.Topic != "STREAM" || r.Type != "STATUS" {
|
||||
return nil, fmt.Errorf("Unknown Reply: %+v\n", r)
|
||||
return nil, fmt.Errorf("Stream Accept Unknown Reply: %+v\n", r)
|
||||
}
|
||||
|
||||
result := r.Pairs["RESULT"]
|
||||
|
Reference in New Issue
Block a user