Compare commits
180 Commits
0.0.201805
...
0.0.201904
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18fa270472 | ||
|
|
f156a53ff4 | ||
|
|
e680008700 | ||
|
|
767c86f8cb | ||
|
|
421c1f9143 | ||
|
|
ac25702eaf | ||
|
|
92f8474832 | ||
|
|
2e0ed4614a | ||
|
|
2fa80c0cb7 | ||
|
|
52ec440d79 | ||
|
|
2faf2dcf90 | ||
|
|
41c30a7279 | ||
|
|
4b1db1d39b | ||
|
|
a80db5e65e | ||
|
|
9748a52073 | ||
|
|
317d716d66 | ||
|
|
6440f010ee | ||
|
|
49ea0c9b1a | ||
|
|
ca59b60aa7 | ||
|
|
c050c6e60f | ||
|
|
91b4e909bb | ||
|
|
2c51d6af48 | ||
|
|
03f2e2614a | ||
|
|
b0e0ab308d | ||
|
|
66fb5caf02 | ||
|
|
3dd9a0535f | ||
|
|
c2a2b8d739 | ||
|
|
70449f1a97 | ||
|
|
33c3528430 | ||
|
|
30ab07e354 | ||
|
|
a6d5ef82f4 | ||
|
|
5c7cc256e3 | ||
|
|
368dea72fe | ||
|
|
9b22255cad | ||
|
|
11f5780250 | ||
|
|
26af6c4651 | ||
|
|
92f72f5aa6 | ||
|
|
1fdf7b19a3 | ||
|
|
a1aabb21ae | ||
|
|
9041d38e2d | ||
|
|
cddfd9a0d8 | ||
|
|
68f0721c6a | ||
|
|
b8e85267cf | ||
|
|
69f0fe67b6 | ||
|
|
d435be35ca | ||
|
|
967d1a0f3d | ||
|
|
88ff67fb6f | ||
|
|
971be13e77 | ||
|
|
366cbd11a4 | ||
|
|
ab0f442daf | ||
|
|
66524c1f7e | ||
|
|
6e4460ae65 | ||
|
|
d002eff155 | ||
|
|
e06a8f8f9f | ||
|
|
ac4944a708 | ||
|
|
2491f9d454 | ||
|
|
8091c6474a | ||
|
|
040da43889 | ||
|
|
b7025b5627 | ||
|
|
6581cfb885 | ||
|
|
4863089120 | ||
|
|
42c6d0e261 | ||
|
|
f7170e5de2 | ||
|
|
b719a09a26 | ||
|
|
f05f52637f | ||
|
|
713477cfb1 | ||
|
|
5981d5cacf | ||
|
|
b13739ada2 | ||
|
|
c4988999ac | ||
|
|
b662896cf4 | ||
|
|
0525f6b112 | ||
|
|
9d830826c5 | ||
|
|
bd963497da | ||
|
|
05d25fd1b7 | ||
|
|
6d2729dccc | ||
|
|
d87cbeeb2f | ||
|
|
043b7e8013 | ||
|
|
ef48d4fa95 | ||
|
|
f7276ed522 | ||
|
|
c4b43e35a7 | ||
|
|
2efafecab5 | ||
|
|
fac1fbcd72 | ||
|
|
52aa00f3ba | ||
|
|
ea59177f1c | ||
|
|
306d08e692 | ||
|
|
3b7a4fa3ef | ||
|
|
223685875f | ||
|
|
652158ec3c | ||
|
|
cb2bc4b34c | ||
|
|
46279ad0f9 | ||
|
|
73df1c0871 | ||
|
|
069016bbc4 | ||
|
|
3c29434a79 | ||
|
|
c599bf9497 | ||
|
|
f7f63765d1 | ||
|
|
3e8f2e3fa5 | ||
|
|
7b636380e5 | ||
|
|
99a3b628e9 | ||
|
|
e7ffce0d21 | ||
|
|
35f72239ac | ||
|
|
c15cbefc12 | ||
|
|
dd998ca86a | ||
|
|
024a4916c2 | ||
|
|
963be8e993 | ||
|
|
e821cdabd2 | ||
|
|
38c7acd70f | ||
|
|
20f1512b7c | ||
|
|
348b4e9f7c | ||
|
|
f81882ee8b | ||
|
|
3e0e61dd26 | ||
|
|
9635a0b3a6 | ||
|
|
90b6938ca0 | ||
|
|
269944002f | ||
|
|
a5a1ece32f | ||
|
|
f1d5db6547 | ||
|
|
dce5192d86 | ||
|
|
955d8dfe04 | ||
|
|
25e18d01e6 | ||
|
|
45959c116a | ||
|
|
d41bc015cc | ||
|
|
31949136df | ||
|
|
6f76edd045 | ||
|
|
3af9aa88a3 | ||
|
|
a5ca02d79a | ||
|
|
2b7562abbb | ||
|
|
89d2c5ed7a | ||
|
|
dff424baf8 | ||
|
|
6e61c369e8 | ||
|
|
8fde8334dc | ||
|
|
a8326ae753 | ||
|
|
05cc0c8298 | ||
|
|
c967f15e44 | ||
|
|
5ace0fdfe2 | ||
|
|
849fa400e9 | ||
|
|
651744561e | ||
|
|
4fd55daafe | ||
|
|
276bf973e8 | ||
|
|
c37c4ece9e | ||
|
|
b803276061 | ||
|
|
8be1fc9c00 | ||
|
|
738d027f0b | ||
|
|
60848b9c72 | ||
|
|
2e772194cf | ||
|
|
85b2378a07 | ||
|
|
fddb949002 | ||
|
|
5d6083df7e | ||
|
|
b41922e5c8 | ||
|
|
dbb72402f2 | ||
|
|
7c971d7ef4 | ||
|
|
70bcf9ecb8 | ||
|
|
ebc7541953 | ||
|
|
833597b585 | ||
|
|
cf81a28dd3 | ||
|
|
942abf948a | ||
|
|
47d1140361 | ||
|
|
39d6e4f2f1 | ||
|
|
1c02557013 | ||
|
|
32d2148835 | ||
|
|
5be541d147 | ||
|
|
063becdc73 | ||
|
|
15da869b31 | ||
|
|
3ad3e83c7a | ||
|
|
2e13b7b0fb | ||
|
|
6b3b1c3b91 | ||
|
|
6a5d0e2bcd | ||
|
|
0ba551807f | ||
|
|
99d5aeeb27 | ||
|
|
a050431f26 | ||
|
|
0c976003c8 | ||
|
|
955e89839f | ||
|
|
a4cd0216c0 | ||
|
|
1d7845a600 | ||
|
|
5079298ce2 | ||
|
|
fc3a7635e5 | ||
|
|
2496cdd8e6 | ||
|
|
4365b4583f | ||
|
|
bbf320c477 | ||
|
|
625d59da14 | ||
|
|
2f2eca8947 | ||
|
|
66f6ca3e4a |
351
COPYING
351
COPYING
@@ -1,338 +1,17 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
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:
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
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.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
Preamble
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
42
Gopkg.lock
generated
42
Gopkg.lock
generated
@@ -1,42 +0,0 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = [
|
||||
"blake2s",
|
||||
"chacha20poly1305",
|
||||
"curve25519",
|
||||
"internal/chacha20",
|
||||
"poly1305"
|
||||
]
|
||||
revision = "1a580b3eff7814fc9b40602fd35256c63b50f491"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"bpf",
|
||||
"internal/iana",
|
||||
"internal/socket",
|
||||
"ipv4",
|
||||
"ipv6"
|
||||
]
|
||||
revision = "9ef9f5bb98a1fdc41f8cf6c250a4404b4085e389"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = [
|
||||
"cpu",
|
||||
"unix"
|
||||
]
|
||||
revision = "88eb85aaee56831ad49eaf7aa80d73de9814cde2"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "d85ae9d2b4afafc3d7535505c46368cbbbec350cf876616302c1bcf44f6ec103"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
15
Gopkg.toml
15
Gopkg.toml
@@ -1,15 +0,0 @@
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
57
Makefile
57
Makefile
@@ -1,51 +1,42 @@
|
||||
PREFIX ?= /usr
|
||||
DESTDIR ?=
|
||||
BINDIR ?= $(PREFIX)/bin
|
||||
export GOPATH ?= $(CURDIR)/.gopath
|
||||
export GO111MODULE := on
|
||||
|
||||
ifeq ($(shell go env GOOS),linux)
|
||||
ifeq ($(wildcard .git),)
|
||||
all: generate-version-and-build
|
||||
|
||||
ifeq ($(shell go env GOOS)|$(wildcard .git),linux|)
|
||||
$(error Do not build this for Linux. Instead use the Linux kernel module. See wireguard.com/install/ for more info.)
|
||||
else
|
||||
$(shell printf 'package main\nconst UseTheKernelModuleInstead = 0xdeadbabe\n' > ireallywantobuildon_linux.go)
|
||||
endif
|
||||
ireallywantobuildon_linux.go:
|
||||
@printf "WARNING: This software is meant for use on non-Linux\nsystems. For Linux, please use the kernel module\ninstead. See wireguard.com/install/ for more info.\n\n" >&2
|
||||
@printf 'package main\nconst UseTheKernelModuleInstead = 0xdeadbabe\n' > "$@"
|
||||
clean-ireallywantobuildon_linux.go:
|
||||
@rm -f ireallywantobuildon_linux.go
|
||||
.PHONY: clean-ireallywantobuildon_linux.go
|
||||
clean: clean-ireallywantobuildon_linux.go
|
||||
wireguard-go: ireallywantobuildon_linux.go
|
||||
endif
|
||||
|
||||
all: wireguard-go
|
||||
MAKEFLAGS += --no-print-directory
|
||||
|
||||
export GOPATH := $(CURDIR)/.gopath
|
||||
export PATH := $(PATH):$(CURDIR)/.gopath/bin
|
||||
GO_IMPORT_PATH := git.zx2c4.com/wireguard-go
|
||||
|
||||
version.go:
|
||||
generate-version-and-build:
|
||||
@export GIT_CEILING_DIRECTORIES="$(realpath $(CURDIR)/..)" && \
|
||||
tag="$$(git describe --dirty 2>/dev/null)" && \
|
||||
ver="$$(printf 'package main\nconst WireGuardGoVersion = "%s"\n' "$$tag")" && \
|
||||
[ "$$(cat $@ 2>/dev/null)" != "$$ver" ] && \
|
||||
echo "$$ver" > $@ && \
|
||||
git update-index --assume-unchanged $@ || true
|
||||
ver="$$(printf 'package device\nconst WireGuardGoVersion = "%s"\n' "$$tag")" && \
|
||||
[ "$$(cat device/version.go 2>/dev/null)" != "$$ver" ] && \
|
||||
echo "$$ver" > device/version.go && \
|
||||
git update-index --assume-unchanged device/version.go || true
|
||||
@$(MAKE) wireguard-go
|
||||
|
||||
.gopath/.created:
|
||||
rm -rf .gopath
|
||||
mkdir -p $(dir .gopath/src/$(GO_IMPORT_PATH))
|
||||
ln -s ../../.. .gopath/src/$(GO_IMPORT_PATH)
|
||||
touch $@
|
||||
|
||||
vendor/.created: Gopkg.toml Gopkg.lock | .gopath/.created
|
||||
command -v dep >/dev/null || go get -v github.com/golang/dep/cmd/dep
|
||||
cd .gopath/src/$(GO_IMPORT_PATH) && dep ensure -vendor-only -v
|
||||
touch $@
|
||||
|
||||
wireguard-go: $(wildcard *.go) $(wildcard */*.go) .gopath/.created vendor/.created version.go
|
||||
go build $(GO_BUILD_EXTRA_ARGS) -v $(GO_IMPORT_PATH)
|
||||
wireguard-go: $(wildcard *.go) $(wildcard */*.go)
|
||||
go build -v -o "$@"
|
||||
|
||||
install: wireguard-go
|
||||
@install -v -d "$(DESTDIR)$(BINDIR)" && install -v -m 0755 wireguard-go "$(DESTDIR)$(BINDIR)/wireguard-go"
|
||||
@install -v -d "$(DESTDIR)$(BINDIR)" && install -v -m 0755 "$<" "$(DESTDIR)$(BINDIR)/wireguard-go"
|
||||
|
||||
clean:
|
||||
rm -f wireguard-go
|
||||
|
||||
update-dep: | .gopath/.created
|
||||
command -v dep >/dev/null || go get -v github.com/golang/dep/cmd/dep
|
||||
cd .gopath/src/$(GO_IMPORT_PATH) && dep ensure -update -v
|
||||
|
||||
.PHONY: clean install update-dep version.go
|
||||
.PHONY: all clean install generate-version-and-build
|
||||
|
||||
39
README.md
39
README.md
@@ -48,7 +48,7 @@ This will run on OpenBSD. It does not yet support sticky sockets. Fwmark is mapp
|
||||
|
||||
## Building
|
||||
|
||||
This requires an installation of [go](https://golang.org) and of [dep](https://github.com/golang/dep). If dep is not installed, it will be downloaded and built as part of the build process.
|
||||
This requires an installation of [go](https://golang.org) ≥ 1.12.
|
||||
|
||||
```
|
||||
$ git clone https://git.zx2c4.com/wireguard-go
|
||||
@@ -58,27 +58,22 @@ $ make
|
||||
|
||||
## License
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation.
|
||||
Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
Additional Permissions For Submission to Apple App Store: Provided that you
|
||||
are otherwise in compliance with the GPLv2 for each covered work you convey
|
||||
(including without limitation making the Corresponding Source available in
|
||||
compliance with Section 3 of the GPLv2), you are granted the additional
|
||||
the additional permission to convey through the Apple App Store
|
||||
non-source executable versions of the Program as incorporated into each
|
||||
applicable covered work as Executable Versions only under the Mozilla
|
||||
Public License version 2.0 (https://www.mozilla.org/en-US/MPL/2.0/).
|
||||
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.
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import "errors"
|
||||
|
||||
34
device/boundif_android.go
Normal file
34
device/boundif_android.go
Normal file
@@ -0,0 +1,34 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
func (device *Device) PeekLookAtSocketFd4() (fd int, err error) {
|
||||
sysconn, err := device.net.bind.(*nativeBind).ipv4.SyscallConn()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = sysconn.Control(func(f uintptr) {
|
||||
fd = int(f)
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (device *Device) PeekLookAtSocketFd6() (fd int, err error) {
|
||||
sysconn, err := device.net.bind.(*nativeBind).ipv6.SyscallConn()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = sysconn.Control(func(f uintptr) {
|
||||
fd = int(f)
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
44
device/boundif_darwin.go
Normal file
44
device/boundif_darwin.go
Normal file
@@ -0,0 +1,44 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func (device *Device) BindSocketToInterface4(interfaceIndex uint32) error {
|
||||
sysconn, err := device.net.bind.(*nativeBind).ipv4.SyscallConn()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
err2 := sysconn.Control(func(fd uintptr) {
|
||||
err = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_BOUND_IF, int(interfaceIndex))
|
||||
})
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (device *Device) BindSocketToInterface6(interfaceIndex uint32) error {
|
||||
sysconn, err := device.net.bind.(*nativeBind).ipv4.SyscallConn()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
err2 := sysconn.Control(func(fd uintptr) {
|
||||
err = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF, int(interfaceIndex))
|
||||
})
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
56
device/boundif_windows.go
Normal file
56
device/boundif_windows.go
Normal file
@@ -0,0 +1,56 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"golang.org/x/sys/windows"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
sockoptIP_UNICAST_IF = 31
|
||||
sockoptIPV6_UNICAST_IF = 31
|
||||
)
|
||||
|
||||
func (device *Device) BindSocketToInterface4(interfaceIndex uint32) error {
|
||||
/* MSDN says for IPv4 this needs to be in net byte order, so that it's like an IP address with leading zeros. */
|
||||
bytes := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(bytes, interfaceIndex)
|
||||
interfaceIndex = *(*uint32)(unsafe.Pointer(&bytes[0]))
|
||||
|
||||
sysconn, err := device.net.bind.(*nativeBind).ipv4.SyscallConn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err2 := sysconn.Control(func(fd uintptr) {
|
||||
err = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, sockoptIP_UNICAST_IF, int(interfaceIndex))
|
||||
})
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (device *Device) BindSocketToInterface6(interfaceIndex uint32) error {
|
||||
sysconn, err := device.net.bind.(*nativeBind).ipv6.SyscallConn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err2 := sysconn.Control(func(fd uintptr) {
|
||||
err = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IPV6, sockoptIPV6_UNICAST_IF, int(interfaceIndex))
|
||||
})
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -79,8 +78,8 @@ func unsafeCloseBind(device *Device) error {
|
||||
|
||||
func (device *Device) BindSetMark(mark uint32) error {
|
||||
|
||||
device.net.mutex.Lock()
|
||||
defer device.net.mutex.Unlock()
|
||||
device.net.Lock()
|
||||
defer device.net.Unlock()
|
||||
|
||||
// check if modified
|
||||
|
||||
@@ -99,23 +98,23 @@ func (device *Device) BindSetMark(mark uint32) error {
|
||||
|
||||
// clear cached source addresses
|
||||
|
||||
device.peers.mutex.RLock()
|
||||
device.peers.RLock()
|
||||
for _, peer := range device.peers.keyMap {
|
||||
peer.mutex.Lock()
|
||||
defer peer.mutex.Unlock()
|
||||
peer.Lock()
|
||||
defer peer.Unlock()
|
||||
if peer.endpoint != nil {
|
||||
peer.endpoint.ClearSrc()
|
||||
}
|
||||
}
|
||||
device.peers.mutex.RUnlock()
|
||||
device.peers.RUnlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (device *Device) BindUpdate() error {
|
||||
|
||||
device.net.mutex.Lock()
|
||||
defer device.net.mutex.Unlock()
|
||||
device.net.Lock()
|
||||
defer device.net.Unlock()
|
||||
|
||||
// close existing sockets
|
||||
|
||||
@@ -149,15 +148,15 @@ func (device *Device) BindUpdate() error {
|
||||
|
||||
// clear cached source addresses
|
||||
|
||||
device.peers.mutex.RLock()
|
||||
device.peers.RLock()
|
||||
for _, peer := range device.peers.keyMap {
|
||||
peer.mutex.Lock()
|
||||
defer peer.mutex.Unlock()
|
||||
peer.Lock()
|
||||
defer peer.Unlock()
|
||||
if peer.endpoint != nil {
|
||||
peer.endpoint.ClearSrc()
|
||||
}
|
||||
}
|
||||
device.peers.mutex.RUnlock()
|
||||
device.peers.RUnlock()
|
||||
|
||||
// start receiving routines
|
||||
|
||||
@@ -174,8 +173,8 @@ func (device *Device) BindUpdate() error {
|
||||
}
|
||||
|
||||
func (device *Device) BindClose() error {
|
||||
device.net.mutex.Lock()
|
||||
device.net.Lock()
|
||||
err := unsafeCloseBind(device)
|
||||
device.net.mutex.Unlock()
|
||||
device.net.Unlock()
|
||||
return err
|
||||
}
|
||||
@@ -1,17 +1,16 @@
|
||||
// +build !linux android
|
||||
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
"net"
|
||||
"runtime"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
/* This code is meant to be a temporary solution
|
||||
@@ -21,14 +20,14 @@ import (
|
||||
* See conn_linux.go for an implementation on the linux platform.
|
||||
*/
|
||||
|
||||
type NativeBind struct {
|
||||
type nativeBind struct {
|
||||
ipv4 *net.UDPConn
|
||||
ipv6 *net.UDPConn
|
||||
}
|
||||
|
||||
type NativeEndpoint net.UDPAddr
|
||||
|
||||
var _ Bind = (*NativeBind)(nil)
|
||||
var _ Bind = (*nativeBind)(nil)
|
||||
var _ Endpoint = (*NativeEndpoint)(nil)
|
||||
|
||||
func CreateEndpoint(s string) (Endpoint, error) {
|
||||
@@ -87,36 +86,57 @@ func listenNet(network string, port int) (*net.UDPConn, int, error) {
|
||||
return conn, uaddr.Port, nil
|
||||
}
|
||||
|
||||
func extractErrno(err error) error {
|
||||
opErr, ok := err.(*net.OpError)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
syscallErr, ok := opErr.Err.(*os.SyscallError)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return syscallErr.Err
|
||||
}
|
||||
|
||||
func CreateBind(uport uint16, device *Device) (Bind, uint16, error) {
|
||||
var err error
|
||||
var bind NativeBind
|
||||
var bind nativeBind
|
||||
|
||||
port := int(uport)
|
||||
|
||||
bind.ipv4, port, err = listenNet("udp4", port)
|
||||
if err != nil {
|
||||
if err != nil && extractErrno(err) != syscall.EAFNOSUPPORT {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
bind.ipv6, port, err = listenNet("udp6", port)
|
||||
if err != nil {
|
||||
if err != nil && extractErrno(err) != syscall.EAFNOSUPPORT {
|
||||
bind.ipv4.Close()
|
||||
bind.ipv4 = nil
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return &bind, uint16(port), nil
|
||||
}
|
||||
|
||||
func (bind *NativeBind) Close() error {
|
||||
err1 := bind.ipv4.Close()
|
||||
err2 := bind.ipv6.Close()
|
||||
func (bind *nativeBind) Close() error {
|
||||
var err1, err2 error
|
||||
if bind.ipv4 != nil {
|
||||
err1 = bind.ipv4.Close()
|
||||
}
|
||||
if bind.ipv6 != nil {
|
||||
err2 = bind.ipv6.Close()
|
||||
}
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
return err2
|
||||
}
|
||||
|
||||
func (bind *NativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
|
||||
func (bind *nativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
|
||||
if bind.ipv4 == nil {
|
||||
return 0, nil, syscall.EAFNOSUPPORT
|
||||
}
|
||||
n, endpoint, err := bind.ipv4.ReadFromUDP(buff)
|
||||
if endpoint != nil {
|
||||
endpoint.IP = endpoint.IP.To4()
|
||||
@@ -124,64 +144,27 @@ func (bind *NativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
|
||||
return n, (*NativeEndpoint)(endpoint), err
|
||||
}
|
||||
|
||||
func (bind *NativeBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) {
|
||||
func (bind *nativeBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) {
|
||||
if bind.ipv6 == nil {
|
||||
return 0, nil, syscall.EAFNOSUPPORT
|
||||
}
|
||||
n, endpoint, err := bind.ipv6.ReadFromUDP(buff)
|
||||
return n, (*NativeEndpoint)(endpoint), err
|
||||
}
|
||||
|
||||
func (bind *NativeBind) Send(buff []byte, endpoint Endpoint) error {
|
||||
func (bind *nativeBind) Send(buff []byte, endpoint Endpoint) error {
|
||||
var err error
|
||||
nend := endpoint.(*NativeEndpoint)
|
||||
if nend.IP.To4() != nil {
|
||||
if bind.ipv4 == nil {
|
||||
return syscall.EAFNOSUPPORT
|
||||
}
|
||||
_, err = bind.ipv4.WriteToUDP(buff, (*net.UDPAddr)(nend))
|
||||
} else {
|
||||
if bind.ipv6 == nil {
|
||||
return syscall.EAFNOSUPPORT
|
||||
}
|
||||
_, err = bind.ipv6.WriteToUDP(buff, (*net.UDPAddr)(nend))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
var fwmarkIoctl int
|
||||
|
||||
func init() {
|
||||
switch runtime.GOOS {
|
||||
case "linux", "android":
|
||||
fwmarkIoctl = 36 /* unix.SO_MARK */
|
||||
case "freebsd":
|
||||
fwmarkIoctl = 0x1015 /* unix.SO_USER_COOKIE */
|
||||
case "openbsd":
|
||||
fwmarkIoctl = 0x1021 /* unix.SO_RTABLE */
|
||||
}
|
||||
}
|
||||
|
||||
func (bind *NativeBind) SetMark(mark uint32) error {
|
||||
if fwmarkIoctl == 0 {
|
||||
return nil
|
||||
}
|
||||
fd4, err1 := bind.ipv4.SyscallConn()
|
||||
fd6, err2 := bind.ipv6.SyscallConn()
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
err3 := fd4.Control(func(fd uintptr) {
|
||||
err1 = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, fwmarkIoctl, int(mark))
|
||||
})
|
||||
err4 := fd6.Control(func(fd uintptr) {
|
||||
err2 = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, fwmarkIoctl, int(mark))
|
||||
})
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
if err3 != nil {
|
||||
return err3
|
||||
}
|
||||
if err4 != nil {
|
||||
return err4
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
// +build !android
|
||||
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*
|
||||
* This implements userspace semantics of "sticky sockets", modeled after
|
||||
* WireGuard's kernelspace implementation. This is more or less a straight port
|
||||
@@ -15,18 +14,23 @@
|
||||
* So this code is remains platform dependent.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"git.zx2c4.com/wireguard-go/rwcancel"
|
||||
"errors"
|
||||
"golang.org/x/sys/unix"
|
||||
"golang.zx2c4.com/wireguard/rwcancel"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
FD_ERR = -1
|
||||
)
|
||||
|
||||
type IPv4Source struct {
|
||||
src [4]byte
|
||||
ifindex int32
|
||||
@@ -59,7 +63,7 @@ func (endpoint *NativeEndpoint) dst6() *unix.SockaddrInet6 {
|
||||
return (*unix.SockaddrInet6)(unsafe.Pointer(&endpoint.dst[0]))
|
||||
}
|
||||
|
||||
type NativeBind struct {
|
||||
type nativeBind struct {
|
||||
sock4 int
|
||||
sock6 int
|
||||
netlinkSock int
|
||||
@@ -68,7 +72,7 @@ type NativeBind struct {
|
||||
}
|
||||
|
||||
var _ Endpoint = (*NativeEndpoint)(nil)
|
||||
var _ Bind = (*NativeBind)(nil)
|
||||
var _ Bind = (*nativeBind)(nil)
|
||||
|
||||
func CreateEndpoint(s string) (Endpoint, error) {
|
||||
var end NativeEndpoint
|
||||
@@ -123,9 +127,10 @@ func createNetlinkRouteSocket() (int, error) {
|
||||
|
||||
}
|
||||
|
||||
func CreateBind(port uint16, device *Device) (*NativeBind, uint16, error) {
|
||||
func CreateBind(port uint16, device *Device) (*nativeBind, uint16, error) {
|
||||
var err error
|
||||
var bind NativeBind
|
||||
var bind nativeBind
|
||||
var newPort uint16
|
||||
|
||||
bind.netlinkSock, err = createNetlinkRouteSocket()
|
||||
if err != nil {
|
||||
@@ -139,21 +144,40 @@ func CreateBind(port uint16, device *Device) (*NativeBind, uint16, error) {
|
||||
|
||||
go bind.routineRouteListener(device)
|
||||
|
||||
bind.sock6, port, err = create6(port)
|
||||
// attempt ipv6 bind, update port if succesful
|
||||
|
||||
bind.sock6, newPort, err = create6(port)
|
||||
if err != nil {
|
||||
if err != syscall.EAFNOSUPPORT {
|
||||
bind.netlinkCancel.Cancel()
|
||||
return nil, port, err
|
||||
return nil, 0, err
|
||||
}
|
||||
} else {
|
||||
port = newPort
|
||||
}
|
||||
|
||||
bind.sock4, port, err = create4(port)
|
||||
// attempt ipv4 bind, update port if succesful
|
||||
|
||||
bind.sock4, newPort, err = create4(port)
|
||||
if err != nil {
|
||||
if err != syscall.EAFNOSUPPORT {
|
||||
bind.netlinkCancel.Cancel()
|
||||
unix.Close(bind.sock6)
|
||||
return nil, 0, err
|
||||
}
|
||||
return &bind, port, err
|
||||
} else {
|
||||
port = newPort
|
||||
}
|
||||
|
||||
if bind.sock4 == FD_ERR && bind.sock6 == FD_ERR {
|
||||
return nil, 0, errors.New("ipv4 and ipv6 not supported")
|
||||
}
|
||||
|
||||
return &bind, port, nil
|
||||
}
|
||||
|
||||
func (bind *NativeBind) SetMark(value uint32) error {
|
||||
func (bind *nativeBind) SetMark(value uint32) error {
|
||||
if bind.sock6 != -1 {
|
||||
err := unix.SetsockoptInt(
|
||||
bind.sock6,
|
||||
unix.SOL_SOCKET,
|
||||
@@ -164,8 +188,10 @@ func (bind *NativeBind) SetMark(value uint32) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = unix.SetsockoptInt(
|
||||
if bind.sock4 != -1 {
|
||||
err := unix.SetsockoptInt(
|
||||
bind.sock4,
|
||||
unix.SOL_SOCKET,
|
||||
unix.SO_MARK,
|
||||
@@ -175,6 +201,7 @@ func (bind *NativeBind) SetMark(value uint32) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
bind.lastMark = value
|
||||
return nil
|
||||
@@ -186,10 +213,15 @@ func closeUnblock(fd int) error {
|
||||
return unix.Close(fd)
|
||||
}
|
||||
|
||||
func (bind *NativeBind) Close() error {
|
||||
err1 := closeUnblock(bind.sock6)
|
||||
err2 := closeUnblock(bind.sock4)
|
||||
err3 := bind.netlinkCancel.Cancel()
|
||||
func (bind *nativeBind) Close() error {
|
||||
var err1, err2, err3 error
|
||||
if bind.sock6 != -1 {
|
||||
err1 = closeUnblock(bind.sock6)
|
||||
}
|
||||
if bind.sock4 != -1 {
|
||||
err2 = closeUnblock(bind.sock4)
|
||||
}
|
||||
err3 = bind.netlinkCancel.Cancel()
|
||||
|
||||
if err1 != nil {
|
||||
return err1
|
||||
@@ -200,8 +232,11 @@ func (bind *NativeBind) Close() error {
|
||||
return err3
|
||||
}
|
||||
|
||||
func (bind *NativeBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) {
|
||||
func (bind *nativeBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) {
|
||||
var end NativeEndpoint
|
||||
if bind.sock6 == -1 {
|
||||
return 0, nil, syscall.EAFNOSUPPORT
|
||||
}
|
||||
n, err := receive6(
|
||||
bind.sock6,
|
||||
buff,
|
||||
@@ -210,8 +245,11 @@ func (bind *NativeBind) ReceiveIPv6(buff []byte) (int, Endpoint, error) {
|
||||
return n, &end, err
|
||||
}
|
||||
|
||||
func (bind *NativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
|
||||
func (bind *nativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
|
||||
var end NativeEndpoint
|
||||
if bind.sock4 == -1 {
|
||||
return 0, nil, syscall.EAFNOSUPPORT
|
||||
}
|
||||
n, err := receive4(
|
||||
bind.sock4,
|
||||
buff,
|
||||
@@ -220,11 +258,17 @@ func (bind *NativeBind) ReceiveIPv4(buff []byte) (int, Endpoint, error) {
|
||||
return n, &end, err
|
||||
}
|
||||
|
||||
func (bind *NativeBind) Send(buff []byte, end Endpoint) error {
|
||||
func (bind *nativeBind) Send(buff []byte, end Endpoint) error {
|
||||
nend := end.(*NativeEndpoint)
|
||||
if !nend.isV6 {
|
||||
if bind.sock4 == -1 {
|
||||
return syscall.EAFNOSUPPORT
|
||||
}
|
||||
return send4(bind.sock4, nend, buff)
|
||||
} else {
|
||||
if bind.sock6 == -1 {
|
||||
return syscall.EAFNOSUPPORT
|
||||
}
|
||||
return send6(bind.sock6, nend, buff)
|
||||
}
|
||||
}
|
||||
@@ -312,7 +356,7 @@ func create4(port uint16) (int, uint16, error) {
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return -1, 0, err
|
||||
return FD_ERR, 0, err
|
||||
}
|
||||
|
||||
addr := unix.SockaddrInet4{
|
||||
@@ -343,7 +387,7 @@ func create4(port uint16) (int, uint16, error) {
|
||||
return unix.Bind(fd, &addr)
|
||||
}(); err != nil {
|
||||
unix.Close(fd)
|
||||
return -1, 0, err
|
||||
return FD_ERR, 0, err
|
||||
}
|
||||
|
||||
return fd, uint16(addr.Port), err
|
||||
@@ -360,7 +404,7 @@ func create6(port uint16) (int, uint16, error) {
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return -1, 0, err
|
||||
return FD_ERR, 0, err
|
||||
}
|
||||
|
||||
// set sockopts and bind
|
||||
@@ -402,7 +446,7 @@ func create6(port uint16) (int, uint16, error) {
|
||||
|
||||
}(); err != nil {
|
||||
unix.Close(fd)
|
||||
return -1, 0, err
|
||||
return FD_ERR, 0, err
|
||||
}
|
||||
|
||||
return fd, uint16(addr.Port), err
|
||||
@@ -548,7 +592,7 @@ func receive6(sock int, buff []byte, end *NativeEndpoint) (int, error) {
|
||||
return size, nil
|
||||
}
|
||||
|
||||
func (bind *NativeBind) routineRouteListener(device *Device) {
|
||||
func (bind *nativeBind) routineRouteListener(device *Device) {
|
||||
type peerEndpointPtr struct {
|
||||
peer *Peer
|
||||
endpoint *Endpoint
|
||||
@@ -563,7 +607,7 @@ func (bind *NativeBind) routineRouteListener(device *Device) {
|
||||
var msgn int
|
||||
for {
|
||||
msgn, _, _, _, err = unix.Recvmsg(bind.netlinkSock, msg[:], nil, 0)
|
||||
if err == nil || !rwcancel.ErrorIsEAGAIN(err) {
|
||||
if err == nil || !rwcancel.RetryAfterError(err) {
|
||||
break
|
||||
}
|
||||
if !bind.netlinkCancel.ReadyRead() {
|
||||
@@ -610,17 +654,17 @@ func (bind *NativeBind) routineRouteListener(device *Device) {
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
pePtr.peer.mutex.Lock()
|
||||
pePtr.peer.Lock()
|
||||
if &pePtr.peer.endpoint != pePtr.endpoint {
|
||||
pePtr.peer.mutex.Unlock()
|
||||
pePtr.peer.Unlock()
|
||||
break
|
||||
}
|
||||
if uint32(pePtr.peer.endpoint.(*NativeEndpoint).src4().ifindex) == ifidx {
|
||||
pePtr.peer.mutex.Unlock()
|
||||
pePtr.peer.Unlock()
|
||||
break
|
||||
}
|
||||
pePtr.peer.endpoint.(*NativeEndpoint).ClearSrc()
|
||||
pePtr.peer.mutex.Unlock()
|
||||
pePtr.peer.Unlock()
|
||||
}
|
||||
attr = attr[attrhdr.Len:]
|
||||
}
|
||||
@@ -631,16 +675,16 @@ func (bind *NativeBind) routineRouteListener(device *Device) {
|
||||
reqPeer = make(map[uint32]peerEndpointPtr)
|
||||
reqPeerLock.Unlock()
|
||||
go func() {
|
||||
device.peers.mutex.RLock()
|
||||
device.peers.RLock()
|
||||
i := uint32(1)
|
||||
for _, peer := range device.peers.keyMap {
|
||||
peer.mutex.RLock()
|
||||
peer.RLock()
|
||||
if peer.endpoint == nil || peer.endpoint.(*NativeEndpoint) == nil {
|
||||
peer.mutex.RUnlock()
|
||||
peer.RUnlock()
|
||||
continue
|
||||
}
|
||||
if peer.endpoint.(*NativeEndpoint).isV6 || peer.endpoint.(*NativeEndpoint).src4().ifindex == 0 {
|
||||
peer.mutex.RUnlock()
|
||||
peer.RUnlock()
|
||||
break
|
||||
}
|
||||
nlmsg := struct {
|
||||
@@ -686,14 +730,14 @@ func (bind *NativeBind) routineRouteListener(device *Device) {
|
||||
endpoint: &peer.endpoint,
|
||||
}
|
||||
reqPeerLock.Unlock()
|
||||
peer.mutex.RUnlock()
|
||||
peer.RUnlock()
|
||||
i++
|
||||
_, err := bind.netlinkCancel.Write((*[unsafe.Sizeof(nlmsg)]byte)(unsafe.Pointer(&nlmsg))[:])
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
device.peers.mutex.RUnlock()
|
||||
device.peers.RUnlock()
|
||||
}()
|
||||
}
|
||||
remain = remain[hdr.Len:]
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"time"
|
||||
@@ -27,18 +26,14 @@ const (
|
||||
PaddingMultiple = 16
|
||||
)
|
||||
|
||||
/* Implementation specific constants */
|
||||
|
||||
const (
|
||||
QueueOutboundSize = 1024
|
||||
QueueInboundSize = 1024
|
||||
QueueHandshakeSize = 1024
|
||||
MaxSegmentSize = (1 << 16) - 1 // largest possible UDP datagram
|
||||
MinMessageSize = MessageKeepaliveSize // minimum size of transport message (keepalive)
|
||||
MaxMessageSize = MaxSegmentSize // maximum size of transport message
|
||||
MaxContentSize = MaxSegmentSize - MessageTransportSize // maximum size of transport message content
|
||||
)
|
||||
|
||||
/* Implementation constants */
|
||||
|
||||
const (
|
||||
UnderLoadQueueSize = QueueHandshakeSize / 8
|
||||
UnderLoadAfterTime = time.Second // how long does the device remain under load after detected
|
||||
@@ -1,13 +1,11 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"git.zx2c4.com/wireguard-go/xchacha20poly1305"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"golang.org/x/crypto/blake2s"
|
||||
@@ -17,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
type CookieChecker struct {
|
||||
mutex sync.RWMutex
|
||||
sync.RWMutex
|
||||
mac1 struct {
|
||||
key [blake2s.Size]byte
|
||||
}
|
||||
@@ -29,7 +27,7 @@ type CookieChecker struct {
|
||||
}
|
||||
|
||||
type CookieGenerator struct {
|
||||
mutex sync.RWMutex
|
||||
sync.RWMutex
|
||||
mac1 struct {
|
||||
key [blake2s.Size]byte
|
||||
}
|
||||
@@ -43,8 +41,8 @@ type CookieGenerator struct {
|
||||
}
|
||||
|
||||
func (st *CookieChecker) Init(pk NoisePublicKey) {
|
||||
st.mutex.Lock()
|
||||
defer st.mutex.Unlock()
|
||||
st.Lock()
|
||||
defer st.Unlock()
|
||||
|
||||
// mac1 state
|
||||
|
||||
@@ -68,8 +66,8 @@ func (st *CookieChecker) Init(pk NoisePublicKey) {
|
||||
}
|
||||
|
||||
func (st *CookieChecker) CheckMAC1(msg []byte) bool {
|
||||
st.mutex.RLock()
|
||||
defer st.mutex.RUnlock()
|
||||
st.RLock()
|
||||
defer st.RUnlock()
|
||||
|
||||
size := len(msg)
|
||||
smac2 := size - blake2s.Size128
|
||||
@@ -85,8 +83,8 @@ func (st *CookieChecker) CheckMAC1(msg []byte) bool {
|
||||
}
|
||||
|
||||
func (st *CookieChecker) CheckMAC2(msg []byte, src []byte) bool {
|
||||
st.mutex.RLock()
|
||||
defer st.mutex.RUnlock()
|
||||
st.RLock()
|
||||
defer st.RUnlock()
|
||||
|
||||
if time.Now().Sub(st.mac2.secretSet) > CookieRefreshTime {
|
||||
return false
|
||||
@@ -121,21 +119,21 @@ func (st *CookieChecker) CreateReply(
|
||||
src []byte,
|
||||
) (*MessageCookieReply, error) {
|
||||
|
||||
st.mutex.RLock()
|
||||
st.RLock()
|
||||
|
||||
// refresh cookie secret
|
||||
|
||||
if time.Now().Sub(st.mac2.secretSet) > CookieRefreshTime {
|
||||
st.mutex.RUnlock()
|
||||
st.mutex.Lock()
|
||||
st.RUnlock()
|
||||
st.Lock()
|
||||
_, err := rand.Read(st.mac2.secret[:])
|
||||
if err != nil {
|
||||
st.mutex.Unlock()
|
||||
st.Unlock()
|
||||
return nil, err
|
||||
}
|
||||
st.mac2.secretSet = time.Now()
|
||||
st.mutex.Unlock()
|
||||
st.mutex.RLock()
|
||||
st.Unlock()
|
||||
st.RLock()
|
||||
}
|
||||
|
||||
// derive cookie
|
||||
@@ -160,26 +158,21 @@ func (st *CookieChecker) CreateReply(
|
||||
|
||||
_, err := rand.Read(reply.Nonce[:])
|
||||
if err != nil {
|
||||
st.mutex.RUnlock()
|
||||
st.RUnlock()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
xchacha20poly1305.Encrypt(
|
||||
reply.Cookie[:0],
|
||||
&reply.Nonce,
|
||||
cookie[:],
|
||||
msg[smac1:smac2],
|
||||
&st.mac2.encryptionKey,
|
||||
)
|
||||
xchapoly, _ := chacha20poly1305.NewX(st.mac2.encryptionKey[:])
|
||||
xchapoly.Seal(reply.Cookie[:0], reply.Nonce[:], cookie[:], msg[smac1:smac2])
|
||||
|
||||
st.mutex.RUnlock()
|
||||
st.RUnlock()
|
||||
|
||||
return reply, nil
|
||||
}
|
||||
|
||||
func (st *CookieGenerator) Init(pk NoisePublicKey) {
|
||||
st.mutex.Lock()
|
||||
defer st.mutex.Unlock()
|
||||
st.Lock()
|
||||
defer st.Unlock()
|
||||
|
||||
func() {
|
||||
hash, _ := blake2s.New256(nil)
|
||||
@@ -199,8 +192,8 @@ func (st *CookieGenerator) Init(pk NoisePublicKey) {
|
||||
}
|
||||
|
||||
func (st *CookieGenerator) ConsumeReply(msg *MessageCookieReply) bool {
|
||||
st.mutex.Lock()
|
||||
defer st.mutex.Unlock()
|
||||
st.Lock()
|
||||
defer st.Unlock()
|
||||
|
||||
if !st.mac2.hasLastMAC1 {
|
||||
return false
|
||||
@@ -208,13 +201,8 @@ func (st *CookieGenerator) ConsumeReply(msg *MessageCookieReply) bool {
|
||||
|
||||
var cookie [blake2s.Size128]byte
|
||||
|
||||
_, err := xchacha20poly1305.Decrypt(
|
||||
cookie[:0],
|
||||
&msg.Nonce,
|
||||
msg.Cookie[:],
|
||||
st.mac2.lastMAC1[:],
|
||||
&st.mac2.encryptionKey,
|
||||
)
|
||||
xchapoly, _ := chacha20poly1305.NewX(st.mac2.encryptionKey[:])
|
||||
_, err := xchapoly.Open(cookie[:0], msg.Nonce[:], msg.Cookie[:], st.mac2.lastMAC1[:])
|
||||
|
||||
if err != nil {
|
||||
return false
|
||||
@@ -235,8 +223,8 @@ func (st *CookieGenerator) AddMacs(msg []byte) {
|
||||
mac1 := msg[smac1:smac2]
|
||||
mac2 := msg[smac2:]
|
||||
|
||||
st.mutex.Lock()
|
||||
defer st.mutex.Unlock()
|
||||
st.Lock()
|
||||
defer st.Unlock()
|
||||
|
||||
// set mac1
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -1,14 +1,13 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"git.zx2c4.com/wireguard-go/ratelimiter"
|
||||
"git.zx2c4.com/wireguard-go/tun"
|
||||
"golang.zx2c4.com/wireguard/ratelimiter"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@@ -30,7 +29,7 @@ type Device struct {
|
||||
state struct {
|
||||
starting sync.WaitGroup
|
||||
stopping sync.WaitGroup
|
||||
mutex sync.Mutex
|
||||
sync.Mutex
|
||||
changing AtomicBool
|
||||
current bool
|
||||
}
|
||||
@@ -38,20 +37,20 @@ type Device struct {
|
||||
net struct {
|
||||
starting sync.WaitGroup
|
||||
stopping sync.WaitGroup
|
||||
mutex sync.RWMutex
|
||||
sync.RWMutex
|
||||
bind Bind // bind interface
|
||||
port uint16 // listening port
|
||||
fwmark uint32 // mark value (0 = disabled)
|
||||
}
|
||||
|
||||
staticIdentity struct {
|
||||
mutex sync.RWMutex
|
||||
sync.RWMutex
|
||||
privateKey NoisePrivateKey
|
||||
publicKey NoisePublicKey
|
||||
}
|
||||
|
||||
peers struct {
|
||||
mutex sync.RWMutex
|
||||
sync.RWMutex
|
||||
keyMap map[NoisePublicKey]*Peer
|
||||
}
|
||||
|
||||
@@ -67,7 +66,12 @@ type Device struct {
|
||||
}
|
||||
|
||||
pool struct {
|
||||
messageBuffers sync.Pool
|
||||
messageBufferPool *sync.Pool
|
||||
messageBufferReuseChan chan *[MaxMessageSize]byte
|
||||
inboundElementPool *sync.Pool
|
||||
inboundElementReuseChan chan *QueueInboundElement
|
||||
outboundElementPool *sync.Pool
|
||||
outboundElementReuseChan chan *QueueOutboundElement
|
||||
}
|
||||
|
||||
queue struct {
|
||||
@@ -89,7 +93,7 @@ type Device struct {
|
||||
/* Converts the peer into a "zombie", which remains in the peer map,
|
||||
* but processes no packets and does not exists in the routing table.
|
||||
*
|
||||
* Must hold device.peers.mutex.
|
||||
* Must hold device.peers.Mutex
|
||||
*/
|
||||
func unsafeRemovePeer(device *Device, peer *Peer, key NoisePublicKey) {
|
||||
|
||||
@@ -113,13 +117,13 @@ func deviceUpdateState(device *Device) {
|
||||
|
||||
// compare to current state of device
|
||||
|
||||
device.state.mutex.Lock()
|
||||
device.state.Lock()
|
||||
|
||||
newIsUp := device.isUp.Get()
|
||||
|
||||
if newIsUp == device.state.current {
|
||||
device.state.changing.Set(false)
|
||||
device.state.mutex.Unlock()
|
||||
device.state.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -131,26 +135,29 @@ func deviceUpdateState(device *Device) {
|
||||
device.isUp.Set(false)
|
||||
break
|
||||
}
|
||||
device.peers.mutex.RLock()
|
||||
device.peers.RLock()
|
||||
for _, peer := range device.peers.keyMap {
|
||||
peer.Start()
|
||||
if peer.persistentKeepaliveInterval > 0 {
|
||||
peer.SendKeepalive()
|
||||
}
|
||||
device.peers.mutex.RUnlock()
|
||||
}
|
||||
device.peers.RUnlock()
|
||||
|
||||
case false:
|
||||
device.BindClose()
|
||||
device.peers.mutex.RLock()
|
||||
device.peers.RLock()
|
||||
for _, peer := range device.peers.keyMap {
|
||||
peer.Stop()
|
||||
}
|
||||
device.peers.mutex.RUnlock()
|
||||
device.peers.RUnlock()
|
||||
}
|
||||
|
||||
// update state variables
|
||||
|
||||
device.state.current = newIsUp
|
||||
device.state.changing.Set(false)
|
||||
device.state.mutex.Unlock()
|
||||
device.state.Unlock()
|
||||
|
||||
// check for state change in the mean time
|
||||
|
||||
@@ -195,11 +202,11 @@ func (device *Device) SetPrivateKey(sk NoisePrivateKey) error {
|
||||
|
||||
// lock required resources
|
||||
|
||||
device.staticIdentity.mutex.Lock()
|
||||
defer device.staticIdentity.mutex.Unlock()
|
||||
device.staticIdentity.Lock()
|
||||
defer device.staticIdentity.Unlock()
|
||||
|
||||
device.peers.mutex.Lock()
|
||||
defer device.peers.mutex.Unlock()
|
||||
device.peers.Lock()
|
||||
defer device.peers.Unlock()
|
||||
|
||||
for _, peer := range device.peers.keyMap {
|
||||
peer.handshake.mutex.RLock()
|
||||
@@ -243,14 +250,6 @@ func (device *Device) SetPrivateKey(sk NoisePrivateKey) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (device *Device) GetMessageBuffer() *[MaxMessageSize]byte {
|
||||
return device.pool.messageBuffers.Get().(*[MaxMessageSize]byte)
|
||||
}
|
||||
|
||||
func (device *Device) PutMessageBuffer(msg *[MaxMessageSize]byte) {
|
||||
device.pool.messageBuffers.Put(msg)
|
||||
}
|
||||
|
||||
func NewDevice(tunDevice tun.TUNDevice, logger *Logger) *Device {
|
||||
device := new(Device)
|
||||
|
||||
@@ -275,11 +274,7 @@ func NewDevice(tunDevice tun.TUNDevice, logger *Logger) *Device {
|
||||
device.indexTable.Init()
|
||||
device.allowedips.Reset()
|
||||
|
||||
device.pool.messageBuffers = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new([MaxMessageSize]byte)
|
||||
},
|
||||
}
|
||||
device.PopulatePools()
|
||||
|
||||
// create queues
|
||||
|
||||
@@ -318,15 +313,15 @@ func NewDevice(tunDevice tun.TUNDevice, logger *Logger) *Device {
|
||||
}
|
||||
|
||||
func (device *Device) LookupPeer(pk NoisePublicKey) *Peer {
|
||||
device.peers.mutex.RLock()
|
||||
defer device.peers.mutex.RUnlock()
|
||||
device.peers.RLock()
|
||||
defer device.peers.RUnlock()
|
||||
|
||||
return device.peers.keyMap[pk]
|
||||
}
|
||||
|
||||
func (device *Device) RemovePeer(key NoisePublicKey) {
|
||||
device.peers.mutex.Lock()
|
||||
defer device.peers.mutex.Unlock()
|
||||
device.peers.Lock()
|
||||
defer device.peers.Unlock()
|
||||
|
||||
// stop peer and remove from routing
|
||||
|
||||
@@ -337,8 +332,8 @@ func (device *Device) RemovePeer(key NoisePublicKey) {
|
||||
}
|
||||
|
||||
func (device *Device) RemoveAllPeers() {
|
||||
device.peers.mutex.Lock()
|
||||
defer device.peers.mutex.Unlock()
|
||||
device.peers.Lock()
|
||||
defer device.peers.Unlock()
|
||||
|
||||
for key, peer := range device.peers.keyMap {
|
||||
unsafeRemovePeer(device, peer, key)
|
||||
@@ -375,8 +370,8 @@ func (device *Device) Close() {
|
||||
|
||||
device.log.Info.Println("Device closing")
|
||||
device.state.changing.Set(true)
|
||||
device.state.mutex.Lock()
|
||||
defer device.state.mutex.Unlock()
|
||||
device.state.Lock()
|
||||
defer device.state.Unlock()
|
||||
|
||||
device.tun.device.Close()
|
||||
device.BindClose()
|
||||
@@ -385,10 +380,11 @@ func (device *Device) Close() {
|
||||
|
||||
close(device.signals.stop)
|
||||
|
||||
device.RemoveAllPeers()
|
||||
|
||||
device.state.stopping.Wait()
|
||||
device.FlushPacketQueues()
|
||||
|
||||
device.RemoveAllPeers()
|
||||
device.rate.limiter.Close()
|
||||
|
||||
device.state.changing.Set(false)
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
/* Create two device instances and simulate full WireGuard interaction
|
||||
* without network dependencies
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
@@ -19,7 +18,7 @@ type IndexTableEntry struct {
|
||||
}
|
||||
|
||||
type IndexTable struct {
|
||||
mutex sync.RWMutex
|
||||
sync.RWMutex
|
||||
table map[uint32]IndexTableEntry
|
||||
}
|
||||
|
||||
@@ -30,20 +29,20 @@ func randUint32() (uint32, error) {
|
||||
}
|
||||
|
||||
func (table *IndexTable) Init() {
|
||||
table.mutex.Lock()
|
||||
defer table.mutex.Unlock()
|
||||
table.Lock()
|
||||
defer table.Unlock()
|
||||
table.table = make(map[uint32]IndexTableEntry)
|
||||
}
|
||||
|
||||
func (table *IndexTable) Delete(index uint32) {
|
||||
table.mutex.Lock()
|
||||
defer table.mutex.Unlock()
|
||||
table.Lock()
|
||||
defer table.Unlock()
|
||||
delete(table.table, index)
|
||||
}
|
||||
|
||||
func (table *IndexTable) SwapIndexForKeypair(index uint32, keypair *Keypair) {
|
||||
table.mutex.Lock()
|
||||
defer table.mutex.Unlock()
|
||||
table.Lock()
|
||||
defer table.Unlock()
|
||||
entry, ok := table.table[index]
|
||||
if !ok {
|
||||
return
|
||||
@@ -66,19 +65,19 @@ func (table *IndexTable) NewIndexForHandshake(peer *Peer, handshake *Handshake)
|
||||
|
||||
// check if index used
|
||||
|
||||
table.mutex.RLock()
|
||||
table.RLock()
|
||||
_, ok := table.table[index]
|
||||
table.mutex.RUnlock()
|
||||
table.RUnlock()
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// check again while locked
|
||||
|
||||
table.mutex.Lock()
|
||||
table.Lock()
|
||||
_, found := table.table[index]
|
||||
if found {
|
||||
table.mutex.Unlock()
|
||||
table.Unlock()
|
||||
continue
|
||||
}
|
||||
table.table[index] = IndexTableEntry{
|
||||
@@ -86,13 +85,13 @@ func (table *IndexTable) NewIndexForHandshake(peer *Peer, handshake *Handshake)
|
||||
handshake: handshake,
|
||||
keypair: nil,
|
||||
}
|
||||
table.mutex.Unlock()
|
||||
table.Unlock()
|
||||
return index, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (table *IndexTable) Lookup(id uint32) IndexTableEntry {
|
||||
table.mutex.RLock()
|
||||
defer table.mutex.RUnlock()
|
||||
table.RLock()
|
||||
defer table.RUnlock()
|
||||
return table.table[id]
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"net"
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
@@ -1,14 +1,13 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"git.zx2c4.com/wireguard-go/replay"
|
||||
"crypto/cipher"
|
||||
"golang.zx2c4.com/wireguard/replay"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -32,15 +31,15 @@ type Keypair struct {
|
||||
}
|
||||
|
||||
type Keypairs struct {
|
||||
mutex sync.RWMutex
|
||||
sync.RWMutex
|
||||
current *Keypair
|
||||
previous *Keypair
|
||||
next *Keypair
|
||||
}
|
||||
|
||||
func (kp *Keypairs) Current() *Keypair {
|
||||
kp.mutex.RLock()
|
||||
defer kp.mutex.RUnlock()
|
||||
kp.RLock()
|
||||
defer kp.RUnlock()
|
||||
return kp.current
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"io"
|
||||
12
device/mark_default.go
Normal file
12
device/mark_default.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// +build !linux,!openbsd,!freebsd
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
func (bind *nativeBind) SetMark(mark uint32) error {
|
||||
return nil
|
||||
}
|
||||
64
device/mark_unix.go
Normal file
64
device/mark_unix.go
Normal file
@@ -0,0 +1,64 @@
|
||||
// +build android openbsd freebsd
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var fwmarkIoctl int
|
||||
|
||||
func init() {
|
||||
switch runtime.GOOS {
|
||||
case "linux", "android":
|
||||
fwmarkIoctl = 36 /* unix.SO_MARK */
|
||||
case "freebsd":
|
||||
fwmarkIoctl = 0x1015 /* unix.SO_USER_COOKIE */
|
||||
case "openbsd":
|
||||
fwmarkIoctl = 0x1021 /* unix.SO_RTABLE */
|
||||
}
|
||||
}
|
||||
|
||||
func (bind *nativeBind) SetMark(mark uint32) error {
|
||||
var operr error
|
||||
if fwmarkIoctl == 0 {
|
||||
return nil
|
||||
}
|
||||
if bind.ipv4 != nil {
|
||||
fd, err := bind.ipv4.SyscallConn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = fd.Control(func(fd uintptr) {
|
||||
operr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, fwmarkIoctl, int(mark))
|
||||
})
|
||||
if err == nil {
|
||||
err = operr
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if bind.ipv6 != nil {
|
||||
fd, err := bind.ipv6.SyscallConn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = fd.Control(func(fd uintptr) {
|
||||
operr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, fwmarkIoctl, int(mark))
|
||||
})
|
||||
if err == nil {
|
||||
err = operr
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
@@ -18,11 +17,11 @@ const (
|
||||
)
|
||||
|
||||
type AtomicBool struct {
|
||||
flag int32
|
||||
int32
|
||||
}
|
||||
|
||||
func (a *AtomicBool) Get() bool {
|
||||
return atomic.LoadInt32(&a.flag) == AtomicTrue
|
||||
return atomic.LoadInt32(&a.int32) == AtomicTrue
|
||||
}
|
||||
|
||||
func (a *AtomicBool) Swap(val bool) bool {
|
||||
@@ -30,7 +29,7 @@ func (a *AtomicBool) Swap(val bool) bool {
|
||||
if val {
|
||||
flag = AtomicTrue
|
||||
}
|
||||
return atomic.SwapInt32(&a.flag, flag) == AtomicTrue
|
||||
return atomic.SwapInt32(&a.int32, flag) == AtomicTrue
|
||||
}
|
||||
|
||||
func (a *AtomicBool) Set(val bool) {
|
||||
@@ -38,7 +37,7 @@ func (a *AtomicBool) Set(val bool) {
|
||||
if val {
|
||||
flag = AtomicTrue
|
||||
}
|
||||
atomic.StoreInt32(&a.flag, flag)
|
||||
atomic.StoreInt32(&a.int32, flag)
|
||||
}
|
||||
|
||||
func min(a, b uint) uint {
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
@@ -79,12 +78,14 @@ func setZero(arr []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
func newPrivateKey() (sk NoisePrivateKey, err error) {
|
||||
// clamping: https://cr.yp.to/ecdh.html
|
||||
_, err = rand.Read(sk[:])
|
||||
func (sk *NoisePrivateKey) clamp() {
|
||||
sk[0] &= 248
|
||||
sk[31] &= 127
|
||||
sk[31] |= 64
|
||||
sk[31] = (sk[31] & 127) | 64
|
||||
}
|
||||
|
||||
func newPrivateKey() (sk NoisePrivateKey, err error) {
|
||||
_, err = rand.Read(sk[:])
|
||||
sk.clamp()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2015-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"git.zx2c4.com/wireguard-go/tai64n"
|
||||
"errors"
|
||||
"golang.org/x/crypto/blake2s"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/crypto/poly1305"
|
||||
"golang.zx2c4.com/wireguard/tai64n"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -90,7 +89,7 @@ type MessageTransport struct {
|
||||
type MessageCookieReply struct {
|
||||
Type uint32
|
||||
Receiver uint32
|
||||
Nonce [24]byte
|
||||
Nonce [chacha20poly1305.NonceSizeX]byte
|
||||
Cookie [blake2s.Size128 + poly1305.TagSize]byte
|
||||
}
|
||||
|
||||
@@ -155,8 +154,8 @@ func init() {
|
||||
|
||||
func (device *Device) CreateMessageInitiation(peer *Peer) (*MessageInitiation, error) {
|
||||
|
||||
device.staticIdentity.mutex.RLock()
|
||||
defer device.staticIdentity.mutex.RUnlock()
|
||||
device.staticIdentity.RLock()
|
||||
defer device.staticIdentity.RUnlock()
|
||||
|
||||
handshake := &peer.handshake
|
||||
handshake.mutex.Lock()
|
||||
@@ -242,8 +241,8 @@ func (device *Device) ConsumeMessageInitiation(msg *MessageInitiation) *Peer {
|
||||
return nil
|
||||
}
|
||||
|
||||
device.staticIdentity.mutex.RLock()
|
||||
defer device.staticIdentity.mutex.RUnlock()
|
||||
device.staticIdentity.RLock()
|
||||
defer device.staticIdentity.RUnlock()
|
||||
|
||||
mixHash(&hash, &InitialHash, device.staticIdentity.publicKey[:])
|
||||
mixHash(&hash, &hash, msg.Ephemeral[:])
|
||||
@@ -424,8 +423,8 @@ func (device *Device) ConsumeMessageResponse(msg *MessageResponse) *Peer {
|
||||
|
||||
// lock private key for reading
|
||||
|
||||
device.staticIdentity.mutex.RLock()
|
||||
defer device.staticIdentity.mutex.RUnlock()
|
||||
device.staticIdentity.RLock()
|
||||
defer device.staticIdentity.RUnlock()
|
||||
|
||||
// finish 3-way DH
|
||||
|
||||
@@ -555,8 +554,8 @@ func (peer *Peer) BeginSymmetricSession() error {
|
||||
// rotate key pairs
|
||||
|
||||
keypairs := &peer.keypairs
|
||||
keypairs.mutex.Lock()
|
||||
defer keypairs.mutex.Unlock()
|
||||
keypairs.Lock()
|
||||
defer keypairs.Unlock()
|
||||
|
||||
previous := keypairs.previous
|
||||
next := keypairs.next
|
||||
@@ -587,8 +586,8 @@ func (peer *Peer) ReceivedWithKeypair(receivedKeypair *Keypair) bool {
|
||||
if keypairs.next != receivedKeypair {
|
||||
return false
|
||||
}
|
||||
keypairs.mutex.Lock()
|
||||
defer keypairs.mutex.Unlock()
|
||||
keypairs.Lock()
|
||||
defer keypairs.Unlock()
|
||||
if keypairs.next != receivedKeypair {
|
||||
return false
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
@@ -46,8 +45,10 @@ func (key NoisePrivateKey) Equals(tar NoisePrivateKey) bool {
|
||||
return subtle.ConstantTimeCompare(key[:], tar[:]) == 1
|
||||
}
|
||||
|
||||
func (key *NoisePrivateKey) FromHex(src string) error {
|
||||
return loadExactHex(key[:], src)
|
||||
func (key *NoisePrivateKey) FromHex(src string) (err error) {
|
||||
err = loadExactHex(key[:], src)
|
||||
key.clamp()
|
||||
return
|
||||
}
|
||||
|
||||
func (key NoisePrivateKey) ToHex() string {
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
@@ -20,7 +19,7 @@ const (
|
||||
|
||||
type Peer struct {
|
||||
isRunning AtomicBool
|
||||
mutex sync.RWMutex // Mostly protects endpoint, but is generally taken whenever we modify peer
|
||||
sync.RWMutex // Mostly protects endpoint, but is generally taken whenever we modify peer
|
||||
keypairs Keypairs
|
||||
handshake Handshake
|
||||
device *Device
|
||||
@@ -58,7 +57,7 @@ type Peer struct {
|
||||
}
|
||||
|
||||
routines struct {
|
||||
mutex sync.Mutex // held when stopping / starting routines
|
||||
sync.Mutex // held when stopping / starting routines
|
||||
starting sync.WaitGroup // routines pending start
|
||||
stopping sync.WaitGroup // routines pending stop
|
||||
stop chan struct{} // size 0, stop all go routines in peer
|
||||
@@ -75,11 +74,11 @@ func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
|
||||
|
||||
// lock resources
|
||||
|
||||
device.staticIdentity.mutex.RLock()
|
||||
defer device.staticIdentity.mutex.RUnlock()
|
||||
device.staticIdentity.RLock()
|
||||
defer device.staticIdentity.RUnlock()
|
||||
|
||||
device.peers.mutex.Lock()
|
||||
defer device.peers.mutex.Unlock()
|
||||
device.peers.Lock()
|
||||
defer device.peers.Unlock()
|
||||
|
||||
// check if over limit
|
||||
|
||||
@@ -90,8 +89,8 @@ func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
|
||||
// create peer
|
||||
|
||||
peer := new(Peer)
|
||||
peer.mutex.Lock()
|
||||
defer peer.mutex.Unlock()
|
||||
peer.Lock()
|
||||
defer peer.Unlock()
|
||||
|
||||
peer.cookieGenerator.Init(pk)
|
||||
peer.device = device
|
||||
@@ -127,15 +126,15 @@ func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
|
||||
}
|
||||
|
||||
func (peer *Peer) SendBuffer(buffer []byte) error {
|
||||
peer.device.net.mutex.RLock()
|
||||
defer peer.device.net.mutex.RUnlock()
|
||||
peer.device.net.RLock()
|
||||
defer peer.device.net.RUnlock()
|
||||
|
||||
if peer.device.net.bind == nil {
|
||||
return errors.New("no bind")
|
||||
}
|
||||
|
||||
peer.mutex.RLock()
|
||||
defer peer.mutex.RUnlock()
|
||||
peer.RLock()
|
||||
defer peer.RUnlock()
|
||||
|
||||
if peer.endpoint == nil {
|
||||
return errors.New("no known endpoint for peer")
|
||||
@@ -163,8 +162,8 @@ func (peer *Peer) Start() {
|
||||
|
||||
// prevent simultaneous start/stop operations
|
||||
|
||||
peer.routines.mutex.Lock()
|
||||
defer peer.routines.mutex.Unlock()
|
||||
peer.routines.Lock()
|
||||
defer peer.routines.Unlock()
|
||||
|
||||
if peer.isRunning.Get() {
|
||||
return
|
||||
@@ -208,14 +207,14 @@ func (peer *Peer) ZeroAndFlushAll() {
|
||||
// clear key pairs
|
||||
|
||||
keypairs := &peer.keypairs
|
||||
keypairs.mutex.Lock()
|
||||
keypairs.Lock()
|
||||
device.DeleteKeypair(keypairs.previous)
|
||||
device.DeleteKeypair(keypairs.current)
|
||||
device.DeleteKeypair(keypairs.next)
|
||||
keypairs.previous = nil
|
||||
keypairs.current = nil
|
||||
keypairs.next = nil
|
||||
keypairs.mutex.Unlock()
|
||||
keypairs.Unlock()
|
||||
|
||||
// clear handshake state
|
||||
|
||||
@@ -238,8 +237,8 @@ func (peer *Peer) Stop() {
|
||||
|
||||
peer.routines.starting.Wait()
|
||||
|
||||
peer.routines.mutex.Lock()
|
||||
defer peer.routines.mutex.Unlock()
|
||||
peer.routines.Lock()
|
||||
defer peer.routines.Unlock()
|
||||
|
||||
peer.device.log.Debug.Println(peer, "- Stopping...")
|
||||
|
||||
@@ -258,3 +257,14 @@ func (peer *Peer) Stop() {
|
||||
|
||||
peer.ZeroAndFlushAll()
|
||||
}
|
||||
|
||||
var RoamingDisabled bool
|
||||
|
||||
func (peer *Peer) SetEndpointFromPacket(endpoint Endpoint) {
|
||||
if RoamingDisabled {
|
||||
return
|
||||
}
|
||||
peer.Lock()
|
||||
peer.endpoint = endpoint
|
||||
peer.Unlock()
|
||||
}
|
||||
89
device/pools.go
Normal file
89
device/pools.go
Normal file
@@ -0,0 +1,89 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
import "sync"
|
||||
|
||||
func (device *Device) PopulatePools() {
|
||||
if PreallocatedBuffersPerPool == 0 {
|
||||
device.pool.messageBufferPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new([MaxMessageSize]byte)
|
||||
},
|
||||
}
|
||||
device.pool.inboundElementPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(QueueInboundElement)
|
||||
},
|
||||
}
|
||||
device.pool.outboundElementPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(QueueOutboundElement)
|
||||
},
|
||||
}
|
||||
} else {
|
||||
device.pool.messageBufferReuseChan = make(chan *[MaxMessageSize]byte, PreallocatedBuffersPerPool)
|
||||
for i := 0; i < PreallocatedBuffersPerPool; i += 1 {
|
||||
device.pool.messageBufferReuseChan <- new([MaxMessageSize]byte)
|
||||
}
|
||||
device.pool.inboundElementReuseChan = make(chan *QueueInboundElement, PreallocatedBuffersPerPool)
|
||||
for i := 0; i < PreallocatedBuffersPerPool; i += 1 {
|
||||
device.pool.inboundElementReuseChan <- new(QueueInboundElement)
|
||||
}
|
||||
device.pool.outboundElementReuseChan = make(chan *QueueOutboundElement, PreallocatedBuffersPerPool)
|
||||
for i := 0; i < PreallocatedBuffersPerPool; i += 1 {
|
||||
device.pool.outboundElementReuseChan <- new(QueueOutboundElement)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (device *Device) GetMessageBuffer() *[MaxMessageSize]byte {
|
||||
if PreallocatedBuffersPerPool == 0 {
|
||||
return device.pool.messageBufferPool.Get().(*[MaxMessageSize]byte)
|
||||
} else {
|
||||
return <-device.pool.messageBufferReuseChan
|
||||
}
|
||||
}
|
||||
|
||||
func (device *Device) PutMessageBuffer(msg *[MaxMessageSize]byte) {
|
||||
if PreallocatedBuffersPerPool == 0 {
|
||||
device.pool.messageBufferPool.Put(msg)
|
||||
} else {
|
||||
device.pool.messageBufferReuseChan <- msg
|
||||
}
|
||||
}
|
||||
|
||||
func (device *Device) GetInboundElement() *QueueInboundElement {
|
||||
if PreallocatedBuffersPerPool == 0 {
|
||||
return device.pool.inboundElementPool.Get().(*QueueInboundElement)
|
||||
} else {
|
||||
return <-device.pool.inboundElementReuseChan
|
||||
}
|
||||
}
|
||||
|
||||
func (device *Device) PutInboundElement(msg *QueueInboundElement) {
|
||||
if PreallocatedBuffersPerPool == 0 {
|
||||
device.pool.inboundElementPool.Put(msg)
|
||||
} else {
|
||||
device.pool.inboundElementReuseChan <- msg
|
||||
}
|
||||
}
|
||||
|
||||
func (device *Device) GetOutboundElement() *QueueOutboundElement {
|
||||
if PreallocatedBuffersPerPool == 0 {
|
||||
return device.pool.outboundElementPool.Get().(*QueueOutboundElement)
|
||||
} else {
|
||||
return <-device.pool.outboundElementReuseChan
|
||||
}
|
||||
}
|
||||
|
||||
func (device *Device) PutOutboundElement(msg *QueueOutboundElement) {
|
||||
if PreallocatedBuffersPerPool == 0 {
|
||||
device.pool.outboundElementPool.Put(msg)
|
||||
} else {
|
||||
device.pool.outboundElementReuseChan <- msg
|
||||
}
|
||||
}
|
||||
16
device/queueconstants_android.go
Normal file
16
device/queueconstants_android.go
Normal file
@@ -0,0 +1,16 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
/* Reduce memory consumption for Android */
|
||||
|
||||
const (
|
||||
QueueOutboundSize = 1024
|
||||
QueueInboundSize = 1024
|
||||
QueueHandshakeSize = 1024
|
||||
MaxSegmentSize = 2200
|
||||
PreallocatedBuffersPerPool = 4096
|
||||
)
|
||||
16
device/queueconstants_default.go
Normal file
16
device/queueconstants_default.go
Normal file
@@ -0,0 +1,16 @@
|
||||
// +build !android,!ios
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
const (
|
||||
QueueOutboundSize = 1024
|
||||
QueueInboundSize = 1024
|
||||
QueueHandshakeSize = 1024
|
||||
MaxSegmentSize = (1 << 16) - 1 // largest possible UDP datagram
|
||||
PreallocatedBuffersPerPool = 0 // Disable and allow for infinite memory growth
|
||||
)
|
||||
18
device/queueconstants_ios.go
Normal file
18
device/queueconstants_ios.go
Normal file
@@ -0,0 +1,18 @@
|
||||
// +build ios
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package device
|
||||
|
||||
/* Fit within memory limits for iOS's Network Extension API, which has stricter requirements */
|
||||
|
||||
const (
|
||||
QueueOutboundSize = 1024
|
||||
QueueInboundSize = 1024
|
||||
QueueHandshakeSize = 1024
|
||||
MaxSegmentSize = 1700
|
||||
PreallocatedBuffersPerPool = 1024
|
||||
)
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -28,7 +27,7 @@ type QueueHandshakeElement struct {
|
||||
|
||||
type QueueInboundElement struct {
|
||||
dropped int32
|
||||
mutex sync.Mutex
|
||||
sync.Mutex
|
||||
buffer *[MaxMessageSize]byte
|
||||
packet []byte
|
||||
counter uint64
|
||||
@@ -44,59 +43,29 @@ func (elem *QueueInboundElement) IsDropped() bool {
|
||||
return atomic.LoadInt32(&elem.dropped) == AtomicTrue
|
||||
}
|
||||
|
||||
func (device *Device) addToInboundQueue(
|
||||
queue chan *QueueInboundElement,
|
||||
element *QueueInboundElement,
|
||||
) {
|
||||
for {
|
||||
func (device *Device) addToInboundAndDecryptionQueues(inboundQueue chan *QueueInboundElement, decryptionQueue chan *QueueInboundElement, element *QueueInboundElement) bool {
|
||||
select {
|
||||
case queue <- element:
|
||||
return
|
||||
default:
|
||||
case inboundQueue <- element:
|
||||
select {
|
||||
case old := <-queue:
|
||||
old.Drop()
|
||||
case decryptionQueue <- element:
|
||||
return true
|
||||
default:
|
||||
element.Drop()
|
||||
element.Unlock()
|
||||
return false
|
||||
}
|
||||
}
|
||||
default:
|
||||
device.PutInboundElement(element)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (device *Device) addToDecryptionQueue(
|
||||
queue chan *QueueInboundElement,
|
||||
element *QueueInboundElement,
|
||||
) {
|
||||
for {
|
||||
func (device *Device) addToHandshakeQueue(queue chan QueueHandshakeElement, element QueueHandshakeElement) bool {
|
||||
select {
|
||||
case queue <- element:
|
||||
return
|
||||
return true
|
||||
default:
|
||||
select {
|
||||
case old := <-queue:
|
||||
// drop & release to potential consumer
|
||||
old.Drop()
|
||||
old.mutex.Unlock()
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (device *Device) addToHandshakeQueue(
|
||||
queue chan QueueHandshakeElement,
|
||||
element QueueHandshakeElement,
|
||||
) {
|
||||
for {
|
||||
select {
|
||||
case queue <- element:
|
||||
return
|
||||
default:
|
||||
select {
|
||||
case elem := <-queue:
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
default:
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +97,7 @@ func (device *Device) RoutineReceiveIncoming(IP int, bind Bind) {
|
||||
device.net.stopping.Done()
|
||||
}()
|
||||
|
||||
logDebug.Println("Routine: receive incoming IPv" + strconv.Itoa(IP) + " - starting")
|
||||
logDebug.Println("Routine: receive incoming IPv" + strconv.Itoa(IP) + " - started")
|
||||
device.net.starting.Done()
|
||||
|
||||
// receive datagrams until conn is closed
|
||||
@@ -155,6 +124,7 @@ func (device *Device) RoutineReceiveIncoming(IP int, bind Bind) {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
device.PutMessageBuffer(buffer)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -177,7 +147,7 @@ func (device *Device) RoutineReceiveIncoming(IP int, bind Bind) {
|
||||
|
||||
// check size
|
||||
|
||||
if len(packet) < MessageTransportType {
|
||||
if len(packet) < MessageTransportSize {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -199,24 +169,24 @@ func (device *Device) RoutineReceiveIncoming(IP int, bind Bind) {
|
||||
}
|
||||
|
||||
// create work element
|
||||
|
||||
peer := value.peer
|
||||
elem := &QueueInboundElement{
|
||||
packet: packet,
|
||||
buffer: buffer,
|
||||
keypair: keypair,
|
||||
dropped: AtomicFalse,
|
||||
endpoint: endpoint,
|
||||
}
|
||||
elem.mutex.Lock()
|
||||
elem := device.GetInboundElement()
|
||||
elem.packet = packet
|
||||
elem.buffer = buffer
|
||||
elem.keypair = keypair
|
||||
elem.dropped = AtomicFalse
|
||||
elem.endpoint = endpoint
|
||||
elem.counter = 0
|
||||
elem.Mutex = sync.Mutex{}
|
||||
elem.Lock()
|
||||
|
||||
// add to decryption queues
|
||||
|
||||
if peer.isRunning.Get() {
|
||||
device.addToDecryptionQueue(device.queue.decryption, elem)
|
||||
device.addToInboundQueue(peer.queue.inbound, elem)
|
||||
if device.addToInboundAndDecryptionQueues(peer.queue.inbound, device.queue.decryption, elem) {
|
||||
buffer = device.GetMessageBuffer()
|
||||
}
|
||||
}
|
||||
|
||||
continue
|
||||
|
||||
@@ -236,7 +206,7 @@ func (device *Device) RoutineReceiveIncoming(IP int, bind Bind) {
|
||||
}
|
||||
|
||||
if okay {
|
||||
device.addToHandshakeQueue(
|
||||
if (device.addToHandshakeQueue(
|
||||
device.queue.handshake,
|
||||
QueueHandshakeElement{
|
||||
msgType: msgType,
|
||||
@@ -244,10 +214,11 @@ func (device *Device) RoutineReceiveIncoming(IP int, bind Bind) {
|
||||
packet: packet,
|
||||
endpoint: endpoint,
|
||||
},
|
||||
)
|
||||
)) {
|
||||
buffer = device.GetMessageBuffer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (device *Device) RoutineDecryption() {
|
||||
@@ -308,8 +279,9 @@ func (device *Device) RoutineDecryption() {
|
||||
)
|
||||
if err != nil {
|
||||
elem.Drop()
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
}
|
||||
elem.mutex.Unlock()
|
||||
elem.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -322,18 +294,26 @@ func (device *Device) RoutineHandshake() {
|
||||
logError := device.log.Error
|
||||
logDebug := device.log.Debug
|
||||
|
||||
var elem QueueHandshakeElement
|
||||
var ok bool
|
||||
|
||||
defer func() {
|
||||
logDebug.Println("Routine: handshake worker - stopped")
|
||||
device.state.stopping.Done()
|
||||
if elem.buffer != nil {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
}
|
||||
}()
|
||||
|
||||
logDebug.Println("Routine: handshake worker - started")
|
||||
device.state.starting.Done()
|
||||
|
||||
var elem QueueHandshakeElement
|
||||
var ok bool
|
||||
|
||||
for {
|
||||
if elem.buffer != nil {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
elem.buffer = nil
|
||||
}
|
||||
|
||||
select {
|
||||
case elem, ok = <-device.queue.handshake:
|
||||
case <-device.signals.stop:
|
||||
@@ -371,7 +351,10 @@ func (device *Device) RoutineHandshake() {
|
||||
// consume reply
|
||||
|
||||
if peer := entry.peer; peer.isRunning.Get() {
|
||||
peer.cookieGenerator.ConsumeReply(&reply)
|
||||
logDebug.Println("Receiving cookie response from ", elem.endpoint.DstToString())
|
||||
if !peer.cookieGenerator.ConsumeReply(&reply) {
|
||||
logDebug.Println("Could not decrypt invalid cookie response")
|
||||
}
|
||||
}
|
||||
|
||||
continue
|
||||
@@ -440,10 +423,7 @@ func (device *Device) RoutineHandshake() {
|
||||
peer.timersAnyAuthenticatedPacketReceived()
|
||||
|
||||
// update endpoint
|
||||
|
||||
peer.mutex.Lock()
|
||||
peer.endpoint = elem.endpoint
|
||||
peer.mutex.Unlock()
|
||||
peer.SetEndpointFromPacket(elem.endpoint)
|
||||
|
||||
logDebug.Println(peer, "- Received handshake initiation")
|
||||
|
||||
@@ -473,10 +453,7 @@ func (device *Device) RoutineHandshake() {
|
||||
}
|
||||
|
||||
// update endpoint
|
||||
|
||||
peer.mutex.Lock()
|
||||
peer.endpoint = elem.endpoint
|
||||
peer.mutex.Unlock()
|
||||
peer.SetEndpointFromPacket(elem.endpoint)
|
||||
|
||||
logDebug.Println(peer, "- Received handshake response")
|
||||
|
||||
@@ -505,6 +482,33 @@ func (device *Device) RoutineHandshake() {
|
||||
}
|
||||
}
|
||||
|
||||
func (peer *Peer) elementStopOrFlush(shouldFlush *bool) (stop bool, elemOk bool, elem *QueueInboundElement) {
|
||||
if !*shouldFlush {
|
||||
select {
|
||||
case <-peer.routines.stop:
|
||||
stop = true
|
||||
return
|
||||
case elem, elemOk = <-peer.queue.inbound:
|
||||
return
|
||||
}
|
||||
} else {
|
||||
select {
|
||||
case <-peer.routines.stop:
|
||||
stop = true
|
||||
return
|
||||
case elem, elemOk = <-peer.queue.inbound:
|
||||
return
|
||||
default:
|
||||
*shouldFlush = false
|
||||
err := peer.device.tun.device.Flush()
|
||||
if err != nil {
|
||||
peer.device.log.Error.Printf("Unable to flush packets: %v", err)
|
||||
}
|
||||
return peer.elementStopOrFlush(shouldFlush)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (peer *Peer) RoutineSequentialReceiver() {
|
||||
|
||||
device := peer.device
|
||||
@@ -512,9 +516,21 @@ func (peer *Peer) RoutineSequentialReceiver() {
|
||||
logError := device.log.Error
|
||||
logDebug := device.log.Debug
|
||||
|
||||
var elem *QueueInboundElement
|
||||
var ok bool
|
||||
var stop bool
|
||||
|
||||
shouldFlush := false
|
||||
|
||||
defer func() {
|
||||
logDebug.Println(peer, "- Routine: sequential receiver - stopped")
|
||||
peer.routines.stopping.Done()
|
||||
if elem != nil {
|
||||
if !elem.IsDropped() {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
}
|
||||
device.PutInboundElement(elem)
|
||||
}
|
||||
}()
|
||||
|
||||
logDebug.Println(peer, "- Routine: sequential receiver - started")
|
||||
@@ -522,21 +538,22 @@ func (peer *Peer) RoutineSequentialReceiver() {
|
||||
peer.routines.starting.Done()
|
||||
|
||||
for {
|
||||
if elem != nil {
|
||||
if !elem.IsDropped() {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
}
|
||||
device.PutInboundElement(elem)
|
||||
elem = nil
|
||||
}
|
||||
|
||||
select {
|
||||
|
||||
case <-peer.routines.stop:
|
||||
return
|
||||
|
||||
case elem, ok := <-peer.queue.inbound:
|
||||
|
||||
if !ok {
|
||||
stop, ok, elem = peer.elementStopOrFlush(&shouldFlush)
|
||||
if stop || !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// wait for decryption
|
||||
|
||||
elem.mutex.Lock()
|
||||
elem.Lock()
|
||||
|
||||
if elem.IsDropped() {
|
||||
continue
|
||||
@@ -549,10 +566,7 @@ func (peer *Peer) RoutineSequentialReceiver() {
|
||||
}
|
||||
|
||||
// update endpoint
|
||||
|
||||
peer.mutex.Lock()
|
||||
peer.endpoint = elem.endpoint
|
||||
peer.mutex.Unlock()
|
||||
peer.SetEndpointFromPacket(elem.endpoint)
|
||||
|
||||
// check if using new keypair
|
||||
if peer.ReceivedWithKeypair(elem.keypair) {
|
||||
@@ -642,13 +656,12 @@ func (peer *Peer) RoutineSequentialReceiver() {
|
||||
|
||||
offset := MessageTransportOffsetContent
|
||||
atomic.AddUint64(&peer.stats.rxBytes, uint64(len(elem.packet)))
|
||||
_, err := device.tun.device.Write(
|
||||
elem.buffer[:offset+len(elem.packet)],
|
||||
offset)
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
if err != nil {
|
||||
_, err := device.tun.device.Write(elem.buffer[:offset+len(elem.packet)], offset)
|
||||
if err == nil {
|
||||
shouldFlush = true
|
||||
}
|
||||
if err != nil && !device.isClosed.Get() {
|
||||
logError.Println("Failed to write packet to TUN device:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -44,7 +43,7 @@ import (
|
||||
|
||||
type QueueOutboundElement struct {
|
||||
dropped int32
|
||||
mutex sync.Mutex
|
||||
sync.Mutex
|
||||
buffer *[MaxMessageSize]byte // slice holding the packet data
|
||||
packet []byte // slice of "buffer" (always!)
|
||||
nonce uint64 // nonce for encryption
|
||||
@@ -53,10 +52,14 @@ type QueueOutboundElement struct {
|
||||
}
|
||||
|
||||
func (device *Device) NewOutboundElement() *QueueOutboundElement {
|
||||
return &QueueOutboundElement{
|
||||
dropped: AtomicFalse,
|
||||
buffer: device.pool.messageBuffers.Get().(*[MaxMessageSize]byte),
|
||||
}
|
||||
elem := device.GetOutboundElement()
|
||||
elem.dropped = AtomicFalse
|
||||
elem.buffer = device.GetMessageBuffer()
|
||||
elem.Mutex = sync.Mutex{}
|
||||
elem.nonce = 0
|
||||
elem.keypair = nil
|
||||
elem.peer = nil
|
||||
return elem
|
||||
}
|
||||
|
||||
func (elem *QueueOutboundElement) Drop() {
|
||||
@@ -67,10 +70,7 @@ func (elem *QueueOutboundElement) IsDropped() bool {
|
||||
return atomic.LoadInt32(&elem.dropped) == AtomicTrue
|
||||
}
|
||||
|
||||
func addToOutboundQueue(
|
||||
queue chan *QueueOutboundElement,
|
||||
element *QueueOutboundElement,
|
||||
) {
|
||||
func addToNonceQueue(queue chan *QueueOutboundElement, element *QueueOutboundElement, device *Device) {
|
||||
for {
|
||||
select {
|
||||
case queue <- element:
|
||||
@@ -78,30 +78,28 @@ func addToOutboundQueue(
|
||||
default:
|
||||
select {
|
||||
case old := <-queue:
|
||||
old.Drop()
|
||||
device.PutMessageBuffer(old.buffer)
|
||||
device.PutOutboundElement(old)
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addToEncryptionQueue(
|
||||
queue chan *QueueOutboundElement,
|
||||
element *QueueOutboundElement,
|
||||
) {
|
||||
for {
|
||||
func addToOutboundAndEncryptionQueues(outboundQueue chan *QueueOutboundElement, encryptionQueue chan *QueueOutboundElement, element *QueueOutboundElement) {
|
||||
select {
|
||||
case queue <- element:
|
||||
case outboundQueue <- element:
|
||||
select {
|
||||
case encryptionQueue <- element:
|
||||
return
|
||||
default:
|
||||
select {
|
||||
case old := <-queue:
|
||||
// drop & release to potential consumer
|
||||
old.Drop()
|
||||
old.mutex.Unlock()
|
||||
element.Drop()
|
||||
element.peer.device.PutMessageBuffer(element.buffer)
|
||||
element.Unlock()
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
element.peer.device.PutMessageBuffer(element.buffer)
|
||||
element.peer.device.PutOutboundElement(element)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +116,8 @@ func (peer *Peer) SendKeepalive() bool {
|
||||
peer.device.log.Debug.Println(peer, "- Sending keepalive packet")
|
||||
return true
|
||||
default:
|
||||
peer.device.PutMessageBuffer(elem.buffer)
|
||||
peer.device.PutOutboundElement(elem)
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -206,7 +206,7 @@ func (peer *Peer) SendHandshakeResponse() error {
|
||||
|
||||
func (device *Device) SendHandshakeCookie(initiatingElem *QueueHandshakeElement) error {
|
||||
|
||||
device.log.Debug.Println("Sending cookie reply to:", initiatingElem.endpoint.DstToString())
|
||||
device.log.Debug.Println("Sending cookie response for denied handshake message for", initiatingElem.endpoint.DstToString())
|
||||
|
||||
sender := binary.LittleEndian.Uint32(initiatingElem.packet[4:8])
|
||||
reply, err := device.cookieChecker.CreateReply(initiatingElem.packet, sender, initiatingElem.endpoint.DstToBytes())
|
||||
@@ -243,8 +243,6 @@ func (peer *Peer) keepKeyFreshSending() {
|
||||
*/
|
||||
func (device *Device) RoutineReadFromTUN() {
|
||||
|
||||
elem := device.NewOutboundElement()
|
||||
|
||||
logDebug := device.log.Debug
|
||||
logError := device.log.Error
|
||||
|
||||
@@ -256,7 +254,14 @@ func (device *Device) RoutineReadFromTUN() {
|
||||
logDebug.Println("Routine: TUN reader - started")
|
||||
device.state.starting.Done()
|
||||
|
||||
var elem *QueueOutboundElement
|
||||
|
||||
for {
|
||||
if elem != nil {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutOutboundElement(elem)
|
||||
}
|
||||
elem = device.NewOutboundElement()
|
||||
|
||||
// read packet
|
||||
|
||||
@@ -268,6 +273,8 @@ func (device *Device) RoutineReadFromTUN() {
|
||||
logError.Println("Failed to read packet from TUN device:", err)
|
||||
device.Close()
|
||||
}
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutOutboundElement(elem)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -309,8 +316,8 @@ func (device *Device) RoutineReadFromTUN() {
|
||||
if peer.queue.packetInNonceQueueIsAwaitingKey.Get() {
|
||||
peer.SendHandshakeInitiation(false)
|
||||
}
|
||||
addToOutboundQueue(peer.queue.nonce, elem)
|
||||
elem = device.NewOutboundElement()
|
||||
addToNonceQueue(peer.queue.nonce, elem, device)
|
||||
elem = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -334,22 +341,25 @@ func (peer *Peer) RoutineNonce() {
|
||||
device := peer.device
|
||||
logDebug := device.log.Debug
|
||||
|
||||
defer func() {
|
||||
logDebug.Println(peer, "- Routine: nonce worker - stopped")
|
||||
peer.queue.packetInNonceQueueIsAwaitingKey.Set(false)
|
||||
peer.routines.stopping.Done()
|
||||
}()
|
||||
|
||||
flush := func() {
|
||||
for {
|
||||
select {
|
||||
case <-peer.queue.nonce:
|
||||
case elem := <-peer.queue.nonce:
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutOutboundElement(elem)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
flush()
|
||||
logDebug.Println(peer, "- Routine: nonce worker - stopped")
|
||||
peer.queue.packetInNonceQueueIsAwaitingKey.Set(false)
|
||||
peer.routines.stopping.Done()
|
||||
}()
|
||||
|
||||
peer.routines.starting.Done()
|
||||
logDebug.Println(peer, "- Routine: nonce worker - started")
|
||||
|
||||
@@ -403,10 +413,14 @@ func (peer *Peer) RoutineNonce() {
|
||||
logDebug.Println(peer, "- Obtained awaited keypair")
|
||||
|
||||
case <-peer.signals.flushNonceQueue:
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutOutboundElement(elem)
|
||||
flush()
|
||||
goto NextPacket
|
||||
|
||||
case <-peer.routines.stop:
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutOutboundElement(elem)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -421,17 +435,17 @@ func (peer *Peer) RoutineNonce() {
|
||||
|
||||
if elem.nonce >= RejectAfterMessages {
|
||||
atomic.StoreUint64(&keypair.sendNonce, RejectAfterMessages)
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutOutboundElement(elem)
|
||||
goto NextPacket
|
||||
}
|
||||
|
||||
elem.keypair = keypair
|
||||
elem.dropped = AtomicFalse
|
||||
elem.mutex.Lock()
|
||||
elem.Lock()
|
||||
|
||||
// add to parallel and sequential queue
|
||||
|
||||
addToEncryptionQueue(device.queue.encryption, elem)
|
||||
addToOutboundQueue(peer.queue.outbound, elem)
|
||||
addToOutboundAndEncryptionQueues(peer.queue.outbound, device.queue.encryption, elem)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -448,6 +462,19 @@ func (device *Device) RoutineEncryption() {
|
||||
logDebug := device.log.Debug
|
||||
|
||||
defer func() {
|
||||
for {
|
||||
select {
|
||||
case elem, ok := <-device.queue.encryption:
|
||||
if ok && !elem.IsDropped() {
|
||||
elem.Drop()
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
elem.Unlock()
|
||||
}
|
||||
default:
|
||||
goto out
|
||||
}
|
||||
}
|
||||
out:
|
||||
logDebug.Println("Routine: encryption worker - stopped")
|
||||
device.state.stopping.Done()
|
||||
}()
|
||||
@@ -490,11 +517,13 @@ func (device *Device) RoutineEncryption() {
|
||||
// pad content to multiple of 16
|
||||
|
||||
mtu := int(atomic.LoadInt32(&device.tun.mtu))
|
||||
rem := len(elem.packet) % PaddingMultiple
|
||||
if rem > 0 {
|
||||
for i := 0; i < PaddingMultiple-rem && len(elem.packet) < mtu; i++ {
|
||||
elem.packet = append(elem.packet, 0)
|
||||
lastUnit := len(elem.packet) % mtu
|
||||
paddedSize := (lastUnit + PaddingMultiple - 1) & ^(PaddingMultiple - 1)
|
||||
if paddedSize > mtu {
|
||||
paddedSize = mtu
|
||||
}
|
||||
for i := len(elem.packet); i < paddedSize; i++ {
|
||||
elem.packet = append(elem.packet, 0)
|
||||
}
|
||||
|
||||
// encrypt content and release to consumer
|
||||
@@ -506,7 +535,7 @@ func (device *Device) RoutineEncryption() {
|
||||
elem.packet,
|
||||
nil,
|
||||
)
|
||||
elem.mutex.Unlock()
|
||||
elem.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -521,8 +550,24 @@ func (peer *Peer) RoutineSequentialSender() {
|
||||
device := peer.device
|
||||
|
||||
logDebug := device.log.Debug
|
||||
logError := device.log.Error
|
||||
|
||||
defer func() {
|
||||
for {
|
||||
select {
|
||||
case elem, ok := <-peer.queue.outbound:
|
||||
if ok {
|
||||
if !elem.IsDropped() {
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
elem.Drop()
|
||||
}
|
||||
device.PutOutboundElement(elem)
|
||||
}
|
||||
default:
|
||||
goto out
|
||||
}
|
||||
}
|
||||
out:
|
||||
logDebug.Println(peer, "- Routine: sequential sender - stopped")
|
||||
peer.routines.stopping.Done()
|
||||
}()
|
||||
@@ -543,8 +588,9 @@ func (peer *Peer) RoutineSequentialSender() {
|
||||
return
|
||||
}
|
||||
|
||||
elem.mutex.Lock()
|
||||
elem.Lock()
|
||||
if elem.IsDropped() {
|
||||
device.PutOutboundElement(elem)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -556,8 +602,9 @@ func (peer *Peer) RoutineSequentialSender() {
|
||||
length := uint64(len(elem.packet))
|
||||
err := peer.SendBuffer(elem.packet)
|
||||
device.PutMessageBuffer(elem.buffer)
|
||||
device.PutOutboundElement(elem)
|
||||
if err != nil {
|
||||
logDebug.Println("Failed to send authenticated packet to peer", peer)
|
||||
logError.Println(peer, "- Failed to send data packet", err)
|
||||
continue
|
||||
}
|
||||
atomic.AddUint64(&peer.stats.txBytes, length)
|
||||
@@ -1,11 +1,11 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2015-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*
|
||||
* This is based heavily on timers.c from the kernel implementation.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
*/
|
||||
|
||||
type Timer struct {
|
||||
timer *time.Timer
|
||||
*time.Timer
|
||||
modifyingLock sync.RWMutex
|
||||
runningLock sync.Mutex
|
||||
isPending bool
|
||||
@@ -27,7 +27,7 @@ type Timer struct {
|
||||
|
||||
func (peer *Peer) NewTimer(expirationFunction func(*Peer)) *Timer {
|
||||
timer := &Timer{}
|
||||
timer.timer = time.AfterFunc(time.Hour, func() {
|
||||
timer.Timer = time.AfterFunc(time.Hour, func() {
|
||||
timer.runningLock.Lock()
|
||||
|
||||
timer.modifyingLock.Lock()
|
||||
@@ -42,21 +42,21 @@ func (peer *Peer) NewTimer(expirationFunction func(*Peer)) *Timer {
|
||||
expirationFunction(peer)
|
||||
timer.runningLock.Unlock()
|
||||
})
|
||||
timer.timer.Stop()
|
||||
timer.Stop()
|
||||
return timer
|
||||
}
|
||||
|
||||
func (timer *Timer) Mod(d time.Duration) {
|
||||
timer.modifyingLock.Lock()
|
||||
timer.isPending = true
|
||||
timer.timer.Reset(d)
|
||||
timer.Reset(d)
|
||||
timer.modifyingLock.Unlock()
|
||||
}
|
||||
|
||||
func (timer *Timer) Del() {
|
||||
timer.modifyingLock.Lock()
|
||||
timer.isPending = false
|
||||
timer.timer.Stop()
|
||||
timer.Stop()
|
||||
timer.modifyingLock.Unlock()
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ func (peer *Peer) timersActive() bool {
|
||||
|
||||
func expiredRetransmitHandshake(peer *Peer) {
|
||||
if atomic.LoadUint32(&peer.timers.handshakeAttempts) > MaxTimerHandshakes {
|
||||
peer.device.log.Debug.Printf("%s: Handshake did not complete after %d attempts, giving up\n", peer, MaxTimerHandshakes+2)
|
||||
peer.device.log.Debug.Printf("%s - Handshake did not complete after %d attempts, giving up\n", peer, MaxTimerHandshakes+2)
|
||||
|
||||
if peer.timersActive() {
|
||||
peer.timers.sendKeepalive.Del()
|
||||
@@ -98,14 +98,14 @@ func expiredRetransmitHandshake(peer *Peer) {
|
||||
}
|
||||
} else {
|
||||
atomic.AddUint32(&peer.timers.handshakeAttempts, 1)
|
||||
peer.device.log.Debug.Printf("%s: Handshake did not complete after %d seconds, retrying (try %d)\n", peer, int(RekeyTimeout.Seconds()), atomic.LoadUint32(&peer.timers.handshakeAttempts)+1)
|
||||
peer.device.log.Debug.Printf("%s - Handshake did not complete after %d seconds, retrying (try %d)\n", peer, int(RekeyTimeout.Seconds()), atomic.LoadUint32(&peer.timers.handshakeAttempts)+1)
|
||||
|
||||
/* We clear the endpoint address src address, in case this is the cause of trouble. */
|
||||
peer.mutex.Lock()
|
||||
peer.Lock()
|
||||
if peer.endpoint != nil {
|
||||
peer.endpoint.ClearSrc()
|
||||
}
|
||||
peer.mutex.Unlock()
|
||||
peer.Unlock()
|
||||
|
||||
peer.SendHandshakeInitiation(true)
|
||||
}
|
||||
@@ -122,19 +122,19 @@ func expiredSendKeepalive(peer *Peer) {
|
||||
}
|
||||
|
||||
func expiredNewHandshake(peer *Peer) {
|
||||
peer.device.log.Debug.Printf("%s: Retrying handshake because we stopped hearing back after %d seconds\n", peer, int((KeepaliveTimeout + RekeyTimeout).Seconds()))
|
||||
peer.device.log.Debug.Printf("%s - Retrying handshake because we stopped hearing back after %d seconds\n", peer, int((KeepaliveTimeout + RekeyTimeout).Seconds()))
|
||||
/* We clear the endpoint address src address, in case this is the cause of trouble. */
|
||||
peer.mutex.Lock()
|
||||
peer.Lock()
|
||||
if peer.endpoint != nil {
|
||||
peer.endpoint.ClearSrc()
|
||||
}
|
||||
peer.mutex.Unlock()
|
||||
peer.Unlock()
|
||||
peer.SendHandshakeInitiation(false)
|
||||
|
||||
}
|
||||
|
||||
func expiredZeroKeyMaterial(peer *Peer) {
|
||||
peer.device.log.Debug.Printf(":%s Removing all keys, since we haven't received a new one in %d seconds\n", peer, int((RejectAfterTime * 3).Seconds()))
|
||||
peer.device.log.Debug.Printf("%s - Removing all keys, since we haven't received a new one in %d seconds\n", peer, int((RejectAfterTime * 3).Seconds()))
|
||||
peer.ZeroAndFlushAll()
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"git.zx2c4.com/wireguard-go/tun"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package device
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"golang.zx2c4.com/wireguard/ipc"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
@@ -18,23 +18,18 @@ import (
|
||||
)
|
||||
|
||||
type IPCError struct {
|
||||
Code int64
|
||||
int64
|
||||
}
|
||||
|
||||
func (s *IPCError) Error() string {
|
||||
return fmt.Sprintf("IPC error: %d", s.Code)
|
||||
func (s IPCError) Error() string {
|
||||
return fmt.Sprintf("IPC error: %d", s.int64)
|
||||
}
|
||||
|
||||
func (s *IPCError) ErrorCode() int64 {
|
||||
return s.Code
|
||||
func (s IPCError) ErrorCode() int64 {
|
||||
return s.int64
|
||||
}
|
||||
|
||||
func ipcGetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
|
||||
device.log.Debug.Println("UAPI: Processing get operation")
|
||||
|
||||
// create lines
|
||||
|
||||
func (device *Device) IpcGetOperation(socket *bufio.Writer) *IPCError {
|
||||
lines := make([]string, 0, 100)
|
||||
send := func(line string) {
|
||||
lines = append(lines, line)
|
||||
@@ -44,14 +39,14 @@ func ipcGetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
|
||||
// lock required resources
|
||||
|
||||
device.net.mutex.RLock()
|
||||
defer device.net.mutex.RUnlock()
|
||||
device.net.RLock()
|
||||
defer device.net.RUnlock()
|
||||
|
||||
device.staticIdentity.mutex.RLock()
|
||||
defer device.staticIdentity.mutex.RUnlock()
|
||||
device.staticIdentity.RLock()
|
||||
defer device.staticIdentity.RUnlock()
|
||||
|
||||
device.peers.mutex.RLock()
|
||||
defer device.peers.mutex.RUnlock()
|
||||
device.peers.RLock()
|
||||
defer device.peers.RUnlock()
|
||||
|
||||
// serialize device related values
|
||||
|
||||
@@ -70,11 +65,12 @@ func ipcGetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
// serialize each peer state
|
||||
|
||||
for _, peer := range device.peers.keyMap {
|
||||
peer.mutex.RLock()
|
||||
defer peer.mutex.RUnlock()
|
||||
peer.RLock()
|
||||
defer peer.RUnlock()
|
||||
|
||||
send("public_key=" + peer.handshake.remoteStatic.ToHex())
|
||||
send("preshared_key=" + peer.handshake.presharedKey.ToHex())
|
||||
send("protocol_version=1")
|
||||
if peer.endpoint != nil {
|
||||
send("endpoint=" + peer.endpoint.DstToString())
|
||||
}
|
||||
@@ -101,16 +97,14 @@ func ipcGetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
for _, line := range lines {
|
||||
_, err := socket.WriteString(line + "\n")
|
||||
if err != nil {
|
||||
return &IPCError{
|
||||
Code: ipcErrorIO,
|
||||
}
|
||||
return &IPCError{ipc.IpcErrorIO}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
func (device *Device) IpcSetOperation(socket *bufio.Reader) *IPCError {
|
||||
scanner := bufio.NewScanner(socket)
|
||||
logError := device.log.Error
|
||||
logDebug := device.log.Debug
|
||||
@@ -130,7 +124,7 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
}
|
||||
parts := strings.Split(line, "=")
|
||||
if len(parts) != 2 {
|
||||
return &IPCError{Code: ipcErrorProtocol}
|
||||
return &IPCError{ipc.IpcErrorProtocol}
|
||||
}
|
||||
key := parts[0]
|
||||
value := parts[1]
|
||||
@@ -145,7 +139,7 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
err := sk.FromHex(value)
|
||||
if err != nil {
|
||||
logError.Println("Failed to set private_key:", err)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
logDebug.Println("UAPI: Updating private key")
|
||||
device.SetPrivateKey(sk)
|
||||
@@ -157,20 +151,20 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
port, err := strconv.ParseUint(value, 10, 16)
|
||||
if err != nil {
|
||||
logError.Println("Failed to parse listen_port:", err)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
|
||||
// update port and rebind
|
||||
|
||||
logDebug.Println("UAPI: Updating listen port")
|
||||
|
||||
device.net.mutex.Lock()
|
||||
device.net.Lock()
|
||||
device.net.port = uint16(port)
|
||||
device.net.mutex.Unlock()
|
||||
device.net.Unlock()
|
||||
|
||||
if err := device.BindUpdate(); err != nil {
|
||||
logError.Println("Failed to set listen_port:", err)
|
||||
return &IPCError{Code: ipcErrorPortInUse}
|
||||
return &IPCError{ipc.IpcErrorPortInUse}
|
||||
}
|
||||
|
||||
case "fwmark":
|
||||
@@ -187,14 +181,14 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
|
||||
if err != nil {
|
||||
logError.Println("Invalid fwmark", err)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
|
||||
logDebug.Println("UAPI: Updating fwmark")
|
||||
|
||||
if err := device.BindSetMark(uint32(fwmark)); err != nil {
|
||||
logError.Println("Failed to update fwmark:", err)
|
||||
return &IPCError{Code: ipcErrorPortInUse}
|
||||
return &IPCError{ipc.IpcErrorPortInUse}
|
||||
}
|
||||
|
||||
case "public_key":
|
||||
@@ -205,14 +199,14 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
case "replace_peers":
|
||||
if value != "true" {
|
||||
logError.Println("Failed to set replace_peers, invalid value:", value)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
logDebug.Println("UAPI: Removing all peers")
|
||||
device.RemoveAllPeers()
|
||||
|
||||
default:
|
||||
logError.Println("Invalid UAPI device key:", key)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,14 +221,14 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
err := publicKey.FromHex(value)
|
||||
if err != nil {
|
||||
logError.Println("Failed to get peer by public key:", err)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
|
||||
// ignore peer with public key of device
|
||||
|
||||
device.staticIdentity.mutex.RLock()
|
||||
device.staticIdentity.RLock()
|
||||
dummy = device.staticIdentity.publicKey.Equals(publicKey)
|
||||
device.staticIdentity.mutex.RUnlock()
|
||||
device.staticIdentity.RUnlock()
|
||||
|
||||
if dummy {
|
||||
peer = &Peer{}
|
||||
@@ -246,7 +240,7 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
peer, err = device.NewPeer(publicKey)
|
||||
if err != nil {
|
||||
logError.Println("Failed to create new peer:", err)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
logDebug.Println(peer, "- UAPI: Created")
|
||||
}
|
||||
@@ -257,7 +251,7 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
|
||||
if value != "true" {
|
||||
logError.Println("Failed to set remove, invalid value:", value)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
if !dummy {
|
||||
logDebug.Println(peer, "- UAPI: Removing")
|
||||
@@ -278,7 +272,7 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
|
||||
if err != nil {
|
||||
logError.Println("Failed to set preshared key:", err)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
|
||||
case "endpoint":
|
||||
@@ -288,8 +282,8 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
logDebug.Println(peer, "- UAPI: Updating endpoint")
|
||||
|
||||
err := func() error {
|
||||
peer.mutex.Lock()
|
||||
defer peer.mutex.Unlock()
|
||||
peer.Lock()
|
||||
defer peer.Unlock()
|
||||
endpoint, err := CreateEndpoint(value)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -299,20 +293,20 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
logError.Println("Failed to set endpoint:", value)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
logError.Println("Failed to set endpoint:", err, ":", value)
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
|
||||
case "persistent_keepalive_interval":
|
||||
|
||||
// update persistent keepalive interval
|
||||
|
||||
logDebug.Println(peer, "- UAPI: Updating persistent keepalive interva")
|
||||
logDebug.Println(peer, "- UAPI: Updating persistent keepalive interval")
|
||||
|
||||
secs, err := strconv.ParseUint(value, 10, 16)
|
||||
if err != nil {
|
||||
logError.Println("Failed to set persistent keepalive interval:", err)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
|
||||
old := peer.persistentKeepaliveInterval
|
||||
@@ -323,7 +317,7 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
if old == 0 && secs != 0 {
|
||||
if err != nil {
|
||||
logError.Println("Failed to get tun device status:", err)
|
||||
return &IPCError{Code: ipcErrorIO}
|
||||
return &IPCError{ipc.IpcErrorIO}
|
||||
}
|
||||
if device.isUp.Get() && !dummy {
|
||||
peer.SendKeepalive()
|
||||
@@ -336,7 +330,7 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
|
||||
if value != "true" {
|
||||
logError.Println("Failed to replace allowedips, invalid value:", value)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
|
||||
if dummy {
|
||||
@@ -352,7 +346,7 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
_, network, err := net.ParseCIDR(value)
|
||||
if err != nil {
|
||||
logError.Println("Failed to set allowed ip:", err)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
|
||||
if dummy {
|
||||
@@ -362,9 +356,16 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
ones, _ := network.Mask.Size()
|
||||
device.allowedips.Insert(network.IP, uint(ones), peer)
|
||||
|
||||
case "protocol_version":
|
||||
|
||||
if value != "1" {
|
||||
logError.Println("Invalid protocol version:", value)
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
|
||||
default:
|
||||
logError.Println("Invalid UAPI peer key:", key)
|
||||
return &IPCError{Code: ipcErrorInvalid}
|
||||
return &IPCError{ipc.IpcErrorInvalid}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -372,7 +373,7 @@ func ipcSetOperation(device *Device, socket *bufio.ReadWriter) *IPCError {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ipcHandle(device *Device, socket net.Conn) {
|
||||
func (device *Device) IpcHandle(socket net.Conn) {
|
||||
|
||||
// create buffered read/writer
|
||||
|
||||
@@ -397,12 +398,10 @@ func ipcHandle(device *Device, socket net.Conn) {
|
||||
|
||||
switch op {
|
||||
case "set=1\n":
|
||||
device.log.Debug.Println("UAPI: Set operation")
|
||||
status = ipcSetOperation(device, buffered)
|
||||
status = device.IpcSetOperation(buffered.Reader)
|
||||
|
||||
case "get=1\n":
|
||||
device.log.Debug.Println("UAPI: Get operation")
|
||||
status = ipcGetOperation(device, buffered)
|
||||
status = device.IpcGetOperation(buffered.Writer)
|
||||
|
||||
default:
|
||||
device.log.Error.Println("Invalid UAPI operation:", op)
|
||||
3
device/version.go
Normal file
3
device/version.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package device
|
||||
|
||||
const WireGuardGoVersion = "0.0.20190409"
|
||||
@@ -1,8 +1,8 @@
|
||||
// +build !android
|
||||
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
8
go.mod
Normal file
8
go.mod
Normal file
@@ -0,0 +1,8 @@
|
||||
module golang.zx2c4.com/wireguard
|
||||
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.4.12
|
||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576
|
||||
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53
|
||||
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54
|
||||
)
|
||||
11
go.sum
Normal file
11
go.sum
Normal file
@@ -0,0 +1,11 @@
|
||||
github.com/Microsoft/go-winio v0.4.12 h1:xAfWHN1IrQ0NJ9TBC0KBZoqLjzDTr1ML+4MywiUOryc=
|
||||
github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576 h1:aUX/1G2gFSs4AsJJg2cL3HuoRhCSCz733FE5GUSuaT4=
|
||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53 h1:kcXqo9vE6fsZY5X5Rd7R1l7fTgnWaDCVmln65REefiE=
|
||||
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54 h1:xe1/2UUJRmA9iDglQSlkx8c5n3twv58+K0mPpC2zmhA=
|
||||
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -1,12 +1,11 @@
|
||||
// +build darwin freebsd openbsd
|
||||
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package ipc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -18,12 +17,13 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var socketDirectory = "/var/run/wireguard"
|
||||
|
||||
const (
|
||||
ipcErrorIO = -int64(unix.EIO)
|
||||
ipcErrorProtocol = -int64(unix.EPROTO)
|
||||
ipcErrorInvalid = -int64(unix.EINVAL)
|
||||
ipcErrorPortInUse = -int64(unix.EADDRINUSE)
|
||||
socketDirectory = "/var/run/wireguard"
|
||||
IpcErrorIO = -int64(unix.EIO)
|
||||
IpcErrorProtocol = -int64(unix.EPROTO)
|
||||
IpcErrorInvalid = -int64(unix.EINVAL)
|
||||
IpcErrorPortInUse = -int64(unix.EADDRINUSE)
|
||||
socketName = "%s.sock"
|
||||
)
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package ipc
|
||||
|
||||
import (
|
||||
"git.zx2c4.com/wireguard-go/rwcancel"
|
||||
"errors"
|
||||
"fmt"
|
||||
"golang.org/x/sys/unix"
|
||||
"golang.zx2c4.com/wireguard/rwcancel"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
var socketDirectory = "/var/run/wireguard"
|
||||
|
||||
const (
|
||||
ipcErrorIO = -int64(unix.EIO)
|
||||
ipcErrorProtocol = -int64(unix.EPROTO)
|
||||
ipcErrorInvalid = -int64(unix.EINVAL)
|
||||
ipcErrorPortInUse = -int64(unix.EADDRINUSE)
|
||||
socketDirectory = "/var/run/wireguard"
|
||||
IpcErrorIO = -int64(unix.EIO)
|
||||
IpcErrorProtocol = -int64(unix.EPROTO)
|
||||
IpcErrorInvalid = -int64(unix.EINVAL)
|
||||
IpcErrorPortInUse = -int64(unix.EADDRINUSE)
|
||||
socketName = "%s.sock"
|
||||
)
|
||||
|
||||
87
ipc/uapi_windows.go
Normal file
87
ipc/uapi_windows.go
Normal file
@@ -0,0 +1,87 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package ipc
|
||||
|
||||
import (
|
||||
"github.com/Microsoft/go-winio"
|
||||
"net"
|
||||
)
|
||||
|
||||
//TODO: replace these with actual standard windows error numbers from the win package
|
||||
const (
|
||||
IpcErrorIO = -int64(5)
|
||||
IpcErrorProtocol = -int64(71)
|
||||
IpcErrorInvalid = -int64(22)
|
||||
IpcErrorPortInUse = -int64(98)
|
||||
)
|
||||
|
||||
type UAPIListener struct {
|
||||
listener net.Listener // unix socket listener
|
||||
connNew chan net.Conn
|
||||
connErr chan error
|
||||
kqueueFd int
|
||||
keventFd int
|
||||
}
|
||||
|
||||
func (l *UAPIListener) Accept() (net.Conn, error) {
|
||||
for {
|
||||
select {
|
||||
case conn := <-l.connNew:
|
||||
return conn, nil
|
||||
|
||||
case err := <-l.connErr:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *UAPIListener) Close() error {
|
||||
return l.listener.Close()
|
||||
}
|
||||
|
||||
func (l *UAPIListener) Addr() net.Addr {
|
||||
return l.listener.Addr()
|
||||
}
|
||||
|
||||
func GetSystemSecurityDescriptor() string {
|
||||
//
|
||||
// SDDL encoded.
|
||||
//
|
||||
// (system = SECURITY_NT_AUTHORITY | SECURITY_LOCAL_SYSTEM_RID)
|
||||
// owner: system
|
||||
// grant: GENERIC_ALL to system
|
||||
//
|
||||
return "O:SYD:(A;;GA;;;SY)"
|
||||
}
|
||||
|
||||
func UAPIListen(name string) (net.Listener, error) {
|
||||
config := winio.PipeConfig{
|
||||
SecurityDescriptor: GetSystemSecurityDescriptor(),
|
||||
}
|
||||
listener, err := winio.ListenPipe("\\\\.\\pipe\\WireGuard\\"+name, &config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
uapi := &UAPIListener{
|
||||
listener: listener,
|
||||
connNew: make(chan net.Conn, 1),
|
||||
connErr: make(chan error, 1),
|
||||
}
|
||||
|
||||
go func(l *UAPIListener) {
|
||||
for {
|
||||
conn, err := l.listener.Accept()
|
||||
if err != nil {
|
||||
l.connErr <- err
|
||||
break
|
||||
}
|
||||
l.connNew <- conn
|
||||
}
|
||||
}(uapi)
|
||||
|
||||
return uapi, nil
|
||||
}
|
||||
46
main.go
46
main.go
@@ -1,14 +1,17 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
// +build !windows
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"git.zx2c4.com/wireguard-go/tun"
|
||||
"fmt"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"golang.zx2c4.com/wireguard/ipc"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
@@ -75,7 +78,7 @@ func warning() {
|
||||
|
||||
func main() {
|
||||
if len(os.Args) == 2 && os.Args[1] == "--version" {
|
||||
fmt.Printf("wireguard-go v%s\n\nUserspace WireGuard daemon for %s-%s.\nInformation available at https://www.wireguard.com.\nCopyright (C) Jason A. Donenfeld <Jason@zx2c4.com>.\n", WireGuardGoVersion, runtime.GOOS, runtime.GOARCH)
|
||||
fmt.Printf("wireguard-go v%s\n\nUserspace WireGuard daemon for %s-%s.\nInformation available at https://www.wireguard.com.\nCopyright (C) Jason A. Donenfeld <Jason@zx2c4.com>.\n", device.WireGuardGoVersion, runtime.GOOS, runtime.GOARCH)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -118,15 +121,15 @@ func main() {
|
||||
logLevel := func() int {
|
||||
switch os.Getenv("LOG_LEVEL") {
|
||||
case "debug":
|
||||
return LogLevelDebug
|
||||
return device.LogLevelDebug
|
||||
case "info":
|
||||
return LogLevelInfo
|
||||
return device.LogLevelInfo
|
||||
case "error":
|
||||
return LogLevelError
|
||||
return device.LogLevelError
|
||||
case "silent":
|
||||
return LogLevelSilent
|
||||
return device.LogLevelSilent
|
||||
}
|
||||
return LogLevelInfo
|
||||
return device.LogLevelInfo
|
||||
}()
|
||||
|
||||
// open TUN device (or use supplied fd)
|
||||
@@ -134,7 +137,7 @@ func main() {
|
||||
tun, err := func() (tun.TUNDevice, error) {
|
||||
tunFdStr := os.Getenv(ENV_WG_TUN_FD)
|
||||
if tunFdStr == "" {
|
||||
return tun.CreateTUN(interfaceName, DefaultMTU)
|
||||
return tun.CreateTUN(interfaceName, device.DefaultMTU)
|
||||
}
|
||||
|
||||
// construct tun device from supplied fd
|
||||
@@ -144,8 +147,13 @@ func main() {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = syscall.SetNonblock(int(fd), true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
file := os.NewFile(uintptr(fd), "")
|
||||
return tun.CreateTUNFromFile(file, DefaultMTU)
|
||||
return tun.CreateTUNFromFile(file, device.DefaultMTU)
|
||||
}()
|
||||
|
||||
if err == nil {
|
||||
@@ -155,11 +163,13 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
logger := NewLogger(
|
||||
logger := device.NewLogger(
|
||||
logLevel,
|
||||
fmt.Sprintf("(%s) ", interfaceName),
|
||||
)
|
||||
|
||||
logger.Info.Println("Starting wireguard-go version", device.WireGuardGoVersion)
|
||||
|
||||
logger.Debug.Println("Debug log enabled")
|
||||
|
||||
if err != nil {
|
||||
@@ -172,7 +182,7 @@ func main() {
|
||||
fileUAPI, err := func() (*os.File, error) {
|
||||
uapiFdStr := os.Getenv(ENV_WG_UAPI_FD)
|
||||
if uapiFdStr == "" {
|
||||
return UAPIOpen(interfaceName)
|
||||
return ipc.UAPIOpen(interfaceName)
|
||||
}
|
||||
|
||||
// use supplied fd
|
||||
@@ -198,7 +208,7 @@ func main() {
|
||||
env = append(env, fmt.Sprintf("%s=4", ENV_WG_UAPI_FD))
|
||||
env = append(env, fmt.Sprintf("%s=1", ENV_WG_PROCESS_FOREGROUND))
|
||||
files := [3]*os.File{}
|
||||
if os.Getenv("LOG_LEVEL") != "" && logLevel != LogLevelSilent {
|
||||
if os.Getenv("LOG_LEVEL") != "" && logLevel != device.LogLevelSilent {
|
||||
files[0], _ = os.Open(os.DevNull)
|
||||
files[1] = os.Stdout
|
||||
files[2] = os.Stderr
|
||||
@@ -238,14 +248,14 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
device := NewDevice(tun, logger)
|
||||
device := device.NewDevice(tun, logger)
|
||||
|
||||
logger.Info.Println("Device started")
|
||||
|
||||
errs := make(chan error)
|
||||
term := make(chan os.Signal, 1)
|
||||
|
||||
uapi, err := UAPIListen(interfaceName, fileUAPI)
|
||||
uapi, err := ipc.UAPIListen(interfaceName, fileUAPI)
|
||||
if err != nil {
|
||||
logger.Error.Println("Failed to listen on uapi socket:", err)
|
||||
os.Exit(ExitSetupFailed)
|
||||
@@ -258,7 +268,7 @@ func main() {
|
||||
errs <- err
|
||||
return
|
||||
}
|
||||
go ipcHandle(device, conn)
|
||||
go device.IpcHandle(conn)
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
91
main_windows.go
Normal file
91
main_windows.go
Normal file
@@ -0,0 +1,91 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"golang.zx2c4.com/wireguard/ipc"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
)
|
||||
|
||||
const (
|
||||
ExitSetupSuccess = 0
|
||||
ExitSetupFailed = 1
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) != 2 {
|
||||
os.Exit(ExitSetupFailed)
|
||||
}
|
||||
interfaceName := os.Args[1]
|
||||
|
||||
logger := device.NewLogger(
|
||||
device.LogLevelDebug,
|
||||
fmt.Sprintf("(%s) ", interfaceName),
|
||||
)
|
||||
logger.Info.Println("Starting wireguard-go version", WireGuardGoVersion)
|
||||
logger.Debug.Println("Debug log enabled")
|
||||
|
||||
tun, err := tun.CreateTUN(interfaceName)
|
||||
if err == nil {
|
||||
realInterfaceName, err2 := tun.Name()
|
||||
if err2 == nil {
|
||||
interfaceName = realInterfaceName
|
||||
}
|
||||
} else {
|
||||
logger.Error.Println("Failed to create TUN device:", err)
|
||||
os.Exit(ExitSetupFailed)
|
||||
}
|
||||
|
||||
device := device.NewDevice(tun, logger)
|
||||
device.Up()
|
||||
logger.Info.Println("Device started")
|
||||
|
||||
uapi, err := ipc.UAPIListen(interfaceName)
|
||||
if err != nil {
|
||||
logger.Error.Println("Failed to listen on uapi socket:", err)
|
||||
os.Exit(ExitSetupFailed)
|
||||
}
|
||||
|
||||
errs := make(chan error)
|
||||
term := make(chan os.Signal, 1)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
conn, err := uapi.Accept()
|
||||
if err != nil {
|
||||
errs <- err
|
||||
return
|
||||
}
|
||||
go device.IpcHandle(conn)
|
||||
}
|
||||
}()
|
||||
logger.Info.Println("UAPI listener started")
|
||||
|
||||
// wait for program to terminate
|
||||
|
||||
signal.Notify(term, os.Interrupt)
|
||||
signal.Notify(term, os.Kill)
|
||||
signal.Notify(term, syscall.SIGTERM)
|
||||
|
||||
select {
|
||||
case <-term:
|
||||
case <-errs:
|
||||
case <-device.Wait():
|
||||
}
|
||||
|
||||
// clean up
|
||||
|
||||
uapi.Close()
|
||||
device.Close()
|
||||
|
||||
logger.Info.Println("Shutting down")
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package ratelimiter
|
||||
@@ -20,21 +20,21 @@ const (
|
||||
)
|
||||
|
||||
type RatelimiterEntry struct {
|
||||
mutex sync.Mutex
|
||||
sync.Mutex
|
||||
lastTime time.Time
|
||||
tokens int64
|
||||
}
|
||||
|
||||
type Ratelimiter struct {
|
||||
mutex sync.RWMutex
|
||||
sync.RWMutex
|
||||
stopReset chan struct{}
|
||||
tableIPv4 map[[net.IPv4len]byte]*RatelimiterEntry
|
||||
tableIPv6 map[[net.IPv6len]byte]*RatelimiterEntry
|
||||
}
|
||||
|
||||
func (rate *Ratelimiter) Close() {
|
||||
rate.mutex.Lock()
|
||||
defer rate.mutex.Unlock()
|
||||
rate.Lock()
|
||||
defer rate.Unlock()
|
||||
|
||||
if rate.stopReset != nil {
|
||||
close(rate.stopReset)
|
||||
@@ -42,8 +42,8 @@ func (rate *Ratelimiter) Close() {
|
||||
}
|
||||
|
||||
func (rate *Ratelimiter) Init() {
|
||||
rate.mutex.Lock()
|
||||
defer rate.mutex.Unlock()
|
||||
rate.Lock()
|
||||
defer rate.Unlock()
|
||||
|
||||
// stop any ongoing garbage collection routine
|
||||
|
||||
@@ -71,23 +71,23 @@ func (rate *Ratelimiter) Init() {
|
||||
}
|
||||
case <-ticker.C:
|
||||
func() {
|
||||
rate.mutex.Lock()
|
||||
defer rate.mutex.Unlock()
|
||||
rate.Lock()
|
||||
defer rate.Unlock()
|
||||
|
||||
for key, entry := range rate.tableIPv4 {
|
||||
entry.mutex.Lock()
|
||||
entry.Lock()
|
||||
if time.Now().Sub(entry.lastTime) > garbageCollectTime {
|
||||
delete(rate.tableIPv4, key)
|
||||
}
|
||||
entry.mutex.Unlock()
|
||||
entry.Unlock()
|
||||
}
|
||||
|
||||
for key, entry := range rate.tableIPv6 {
|
||||
entry.mutex.Lock()
|
||||
entry.Lock()
|
||||
if time.Now().Sub(entry.lastTime) > garbageCollectTime {
|
||||
delete(rate.tableIPv6, key)
|
||||
}
|
||||
entry.mutex.Unlock()
|
||||
entry.Unlock()
|
||||
}
|
||||
|
||||
if len(rate.tableIPv4) == 0 && len(rate.tableIPv6) == 0 {
|
||||
@@ -109,7 +109,7 @@ func (rate *Ratelimiter) Allow(ip net.IP) bool {
|
||||
IPv4 := ip.To4()
|
||||
IPv6 := ip.To16()
|
||||
|
||||
rate.mutex.RLock()
|
||||
rate.RLock()
|
||||
|
||||
if IPv4 != nil {
|
||||
copy(keyIPv4[:], IPv4)
|
||||
@@ -119,7 +119,7 @@ func (rate *Ratelimiter) Allow(ip net.IP) bool {
|
||||
entry = rate.tableIPv6[keyIPv6]
|
||||
}
|
||||
|
||||
rate.mutex.RUnlock()
|
||||
rate.RUnlock()
|
||||
|
||||
// make new entry if not found
|
||||
|
||||
@@ -127,7 +127,7 @@ func (rate *Ratelimiter) Allow(ip net.IP) bool {
|
||||
entry = new(RatelimiterEntry)
|
||||
entry.tokens = maxTokens - packetCost
|
||||
entry.lastTime = time.Now()
|
||||
rate.mutex.Lock()
|
||||
rate.Lock()
|
||||
if IPv4 != nil {
|
||||
rate.tableIPv4[keyIPv4] = entry
|
||||
if len(rate.tableIPv4) == 1 && len(rate.tableIPv6) == 0 {
|
||||
@@ -139,13 +139,13 @@ func (rate *Ratelimiter) Allow(ip net.IP) bool {
|
||||
rate.stopReset <- struct{}{}
|
||||
}
|
||||
}
|
||||
rate.mutex.Unlock()
|
||||
rate.Unlock()
|
||||
return true
|
||||
}
|
||||
|
||||
// add tokens to entry
|
||||
|
||||
entry.mutex.Lock()
|
||||
entry.Lock()
|
||||
now := time.Now()
|
||||
entry.tokens += now.Sub(entry.lastTime).Nanoseconds()
|
||||
entry.lastTime = now
|
||||
@@ -157,9 +157,9 @@ func (rate *Ratelimiter) Allow(ip net.IP) bool {
|
||||
|
||||
if entry.tokens > packetCost {
|
||||
entry.tokens -= packetCost
|
||||
entry.mutex.Unlock()
|
||||
entry.Unlock()
|
||||
return true
|
||||
}
|
||||
entry.mutex.Unlock()
|
||||
entry.Unlock()
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package ratelimiter
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package replay
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package replay
|
||||
|
||||
22
rwcancel/fdset.go
Normal file
22
rwcancel/fdset.go
Normal file
@@ -0,0 +1,22 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package rwcancel
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
type fdSet struct {
|
||||
unix.FdSet
|
||||
}
|
||||
|
||||
func (fdset *fdSet) set(i int) {
|
||||
bits := 32 << (^uint(0) >> 63)
|
||||
fdset.Bits[i/bits] |= 1 << uint(i%bits)
|
||||
}
|
||||
|
||||
func (fdset *fdSet) check(i int) bool {
|
||||
bits := 32 << (^uint(0) >> 63)
|
||||
return (fdset.Bits[i/bits] & (1 << uint(i%bits))) != 0
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
// +build !freebsd
|
||||
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package rwcancel
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
type fdSet struct {
|
||||
fdset unix.FdSet
|
||||
}
|
||||
|
||||
func (fdset *fdSet) set(i int) {
|
||||
bits := 32 << (^uint(0) >> 63)
|
||||
fdset.fdset.Bits[i/bits] |= 1 << uint(i%bits)
|
||||
}
|
||||
|
||||
func (fdset *fdSet) check(i int) bool {
|
||||
bits := 32 << (^uint(0) >> 63)
|
||||
return (fdset.fdset.Bits[i/bits] & (1 << uint(i%bits))) != 0
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package rwcancel
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
type fdSet struct {
|
||||
fdset unix.FdSet
|
||||
}
|
||||
|
||||
func (fdset *fdSet) set(i int) {
|
||||
bits := 32 << (^uint(0) >> 63)
|
||||
fdset.fdset.X__fds_bits[i/bits] |= 1 << uint(i%bits)
|
||||
}
|
||||
|
||||
func (fdset *fdSet) check(i int) bool {
|
||||
bits := 32 << (^uint(0) >> 63)
|
||||
return (fdset.fdset.X__fds_bits[i/bits] & (1 << uint(i%bits))) != 0
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package rwcancel
|
||||
@@ -40,15 +40,16 @@ func NewRWCancel(fd int) (*RWCancel, error) {
|
||||
return &rwcancel, nil
|
||||
}
|
||||
|
||||
/* https://golang.org/src/crypto/rand/eagain.go */
|
||||
func ErrorIsEAGAIN(err error) bool {
|
||||
func RetryAfterError(err error) bool {
|
||||
if pe, ok := err.(*os.PathError); ok {
|
||||
if errno, ok := pe.Err.(syscall.Errno); ok && errno == syscall.EAGAIN {
|
||||
err = pe.Err
|
||||
}
|
||||
if errno, ok := err.(syscall.Errno); ok {
|
||||
switch errno {
|
||||
case syscall.EAGAIN, syscall.EINTR:
|
||||
return true
|
||||
}
|
||||
}
|
||||
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EAGAIN {
|
||||
return true
|
||||
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -58,7 +59,7 @@ func (rw *RWCancel) ReadyRead() bool {
|
||||
fdset := fdSet{}
|
||||
fdset.set(rw.fd)
|
||||
fdset.set(closeFd)
|
||||
err := unixSelect(max(rw.fd, closeFd)+1, &fdset.fdset, nil, nil, nil)
|
||||
err := unixSelect(max(rw.fd, closeFd)+1, &fdset.FdSet, nil, nil, nil)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -73,7 +74,7 @@ func (rw *RWCancel) ReadyWrite() bool {
|
||||
fdset := fdSet{}
|
||||
fdset.set(rw.fd)
|
||||
fdset.set(closeFd)
|
||||
err := unixSelect(max(rw.fd, closeFd)+1, nil, &fdset.fdset, nil, nil)
|
||||
err := unixSelect(max(rw.fd, closeFd)+1, nil, &fdset.FdSet, nil, nil)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -86,7 +87,7 @@ func (rw *RWCancel) ReadyWrite() bool {
|
||||
func (rw *RWCancel) Read(p []byte) (n int, err error) {
|
||||
for {
|
||||
n, err := unix.Read(rw.fd, p)
|
||||
if err == nil || !ErrorIsEAGAIN(err) {
|
||||
if err == nil || !RetryAfterError(err) {
|
||||
return n, err
|
||||
}
|
||||
if !rw.ReadyRead() {
|
||||
@@ -98,7 +99,7 @@ func (rw *RWCancel) Read(p []byte) (n int, err error) {
|
||||
func (rw *RWCancel) Write(p []byte) (n int, err error) {
|
||||
for {
|
||||
n, err := unix.Write(rw.fd, p)
|
||||
if err == nil || !ErrorIsEAGAIN(err) {
|
||||
if err == nil || !RetryAfterError(err) {
|
||||
return n, err
|
||||
}
|
||||
if !rw.ReadyWrite() {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// +build !linux
|
||||
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package rwcancel
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package rwcancel
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tai64n
|
||||
@@ -12,7 +12,8 @@ import (
|
||||
)
|
||||
|
||||
const TimestampSize = 12
|
||||
const base = uint64(4611686018427387914)
|
||||
const base = uint64(0x400000000000000a)
|
||||
const whitenerMask = uint32(0x1000000 - 1)
|
||||
|
||||
type Timestamp [TimestampSize]byte
|
||||
|
||||
@@ -20,7 +21,7 @@ func Now() Timestamp {
|
||||
var tai64n Timestamp
|
||||
now := time.Now()
|
||||
secs := base + uint64(now.Unix())
|
||||
nano := uint32(now.UnixNano())
|
||||
nano := uint32(now.Nanosecond()) &^ whitenerMask
|
||||
binary.BigEndian.PutUint64(tai64n[:], secs)
|
||||
binary.BigEndian.PutUint32(tai64n[8:], nano)
|
||||
return tai64n
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tai64n
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
package tun
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
@@ -20,7 +20,7 @@ type DummyTUN struct {
|
||||
name string
|
||||
mtu int
|
||||
packets chan []byte
|
||||
events chan TUNEvent
|
||||
events chan tun.TUNEvent
|
||||
}
|
||||
|
||||
func (tun *DummyTUN) File() *os.File {
|
||||
@@ -46,7 +46,7 @@ func (tun *DummyTUN) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *DummyTUN) Events() chan TUNEvent {
|
||||
func (tun *DummyTUN) Events() chan tun.TUNEvent {
|
||||
return tun.events
|
||||
}
|
||||
|
||||
@@ -59,11 +59,11 @@ func (tun *DummyTUN) Read(d []byte, offset int) (int, error) {
|
||||
return len(t), nil
|
||||
}
|
||||
|
||||
func CreateDummyTUN(name string) (TUNDevice, error) {
|
||||
func CreateDummyTUN(name string) (tun.TUNDevice, error) {
|
||||
var dummy DummyTUN
|
||||
dummy.mtu = 0
|
||||
dummy.packets = make(chan []byte, 100)
|
||||
dummy.events = make(chan TUNEvent, 10)
|
||||
dummy.events = make(chan tun.TUNEvent, 10)
|
||||
return &dummy, nil
|
||||
}
|
||||
|
||||
24
tun/operateonfd.go
Normal file
24
tun/operateonfd.go
Normal file
@@ -0,0 +1,24 @@
|
||||
// +build !windows
|
||||
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func (tun *NativeTun) operateOnFd(fn func(fd uintptr)) {
|
||||
sysconn, err := tun.tunFile.SyscallConn()
|
||||
if err != nil {
|
||||
tun.errors <- fmt.Errorf("unable to find sysconn for tunfile: %s", err.Error())
|
||||
return
|
||||
}
|
||||
err = sysconn.Control(fn)
|
||||
if err != nil {
|
||||
tun.errors <- fmt.Errorf("unable to control sysconn for tunfile: %s", err.Error())
|
||||
}
|
||||
}
|
||||
10
tun/tun.go
10
tun/tun.go
@@ -1,12 +1,13 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tun
|
||||
|
||||
import "os"
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
type TUNEvent int
|
||||
|
||||
@@ -20,6 +21,7 @@ type TUNDevice interface {
|
||||
File() *os.File // returns the file descriptor of the device
|
||||
Read([]byte, int) (int, error) // read a packet from the device (without any additional headers)
|
||||
Write([]byte, int) (int, error) // writes a packet to the device (without any additional headers)
|
||||
Flush() error // flush all previous writes to the device
|
||||
MTU() (int, error) // returns the MTU of the device
|
||||
Name() (string, error) // fetches and returns the current name
|
||||
Events() chan TUNEvent // returns a constant channel of events related to the device
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"git.zx2c4.com/wireguard-go/rwcancel"
|
||||
"errors"
|
||||
"fmt"
|
||||
"golang.org/x/net/ipv6"
|
||||
"golang.org/x/sys/unix"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -33,10 +31,9 @@ type sockaddrCtl struct {
|
||||
scReserved [5]uint32
|
||||
}
|
||||
|
||||
type nativeTun struct {
|
||||
type NativeTun struct {
|
||||
name string
|
||||
fd *os.File
|
||||
rwcancel *rwcancel.RWCancel
|
||||
tunFile *os.File
|
||||
events chan TUNEvent
|
||||
errors chan error
|
||||
routeSocket int
|
||||
@@ -44,7 +41,7 @@ type nativeTun struct {
|
||||
|
||||
var sockaddrCtlSize uintptr = 32
|
||||
|
||||
func (tun *nativeTun) routineRouteListener(tunIfindex int) {
|
||||
func (tun *NativeTun) routineRouteListener(tunIfindex int) {
|
||||
var (
|
||||
statusUp bool
|
||||
statusMTU int
|
||||
@@ -54,8 +51,12 @@ func (tun *nativeTun) routineRouteListener(tunIfindex int) {
|
||||
|
||||
data := make([]byte, os.Getpagesize())
|
||||
for {
|
||||
retry:
|
||||
n, err := unix.Read(tun.routeSocket, data)
|
||||
if err != nil {
|
||||
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EINTR {
|
||||
goto retry
|
||||
}
|
||||
tun.errors <- err
|
||||
return
|
||||
}
|
||||
@@ -150,12 +151,16 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) {
|
||||
return nil, fmt.Errorf("SYS_CONNECT: %v", errno)
|
||||
}
|
||||
|
||||
err = syscall.SetNonblock(fd, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tun, err := CreateTUNFromFile(os.NewFile(uintptr(fd), ""), mtu)
|
||||
|
||||
if err == nil && name == "utun" {
|
||||
fname := os.Getenv("WG_TUN_NAME_FILE")
|
||||
if fname != "" {
|
||||
ioutil.WriteFile(fname, []byte(tun.(*nativeTun).name+"\n"), 0400)
|
||||
ioutil.WriteFile(fname, []byte(tun.(*NativeTun).name+"\n"), 0400)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,16 +168,15 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) {
|
||||
}
|
||||
|
||||
func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
|
||||
|
||||
tun := &nativeTun{
|
||||
fd: file,
|
||||
tun := &NativeTun{
|
||||
tunFile: file,
|
||||
events: make(chan TUNEvent, 10),
|
||||
errors: make(chan error, 1),
|
||||
errors: make(chan error, 5),
|
||||
}
|
||||
|
||||
name, err := tun.Name()
|
||||
if err != nil {
|
||||
tun.fd.Close()
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -184,47 +188,45 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
|
||||
return iface.Index, nil
|
||||
}()
|
||||
if err != nil {
|
||||
tun.fd.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tun.rwcancel, err = rwcancel.NewRWCancel(int(file.Fd()))
|
||||
if err != nil {
|
||||
tun.fd.Close()
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
|
||||
if err != nil {
|
||||
tun.fd.Close()
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go tun.routineRouteListener(tunIfindex)
|
||||
|
||||
if mtu > 0 {
|
||||
err = tun.setMTU(mtu)
|
||||
if err != nil {
|
||||
tun.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return tun, nil
|
||||
}
|
||||
|
||||
func (tun *nativeTun) Name() (string, error) {
|
||||
|
||||
func (tun *NativeTun) Name() (string, error) {
|
||||
var ifName struct {
|
||||
name [16]byte
|
||||
}
|
||||
ifNameSize := uintptr(16)
|
||||
|
||||
_, _, errno := unix.Syscall6(
|
||||
var errno syscall.Errno
|
||||
tun.operateOnFd(func(fd uintptr) {
|
||||
_, _, errno = unix.Syscall6(
|
||||
unix.SYS_GETSOCKOPT,
|
||||
uintptr(tun.fd.Fd()),
|
||||
fd,
|
||||
2, /* #define SYSPROTO_CONTROL 2 */
|
||||
2, /* #define UTUN_OPT_IFNAME 2 */
|
||||
uintptr(unsafe.Pointer(&ifName)),
|
||||
uintptr(unsafe.Pointer(&ifNameSize)), 0)
|
||||
})
|
||||
|
||||
if errno != 0 {
|
||||
return "", fmt.Errorf("SYS_GETSOCKOPT: %v", errno)
|
||||
@@ -234,21 +236,21 @@ func (tun *nativeTun) Name() (string, error) {
|
||||
return tun.name, nil
|
||||
}
|
||||
|
||||
func (tun *nativeTun) File() *os.File {
|
||||
return tun.fd
|
||||
func (tun *NativeTun) File() *os.File {
|
||||
return tun.tunFile
|
||||
}
|
||||
|
||||
func (tun *nativeTun) Events() chan TUNEvent {
|
||||
func (tun *NativeTun) Events() chan TUNEvent {
|
||||
return tun.events
|
||||
}
|
||||
|
||||
func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) {
|
||||
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
|
||||
select {
|
||||
case err := <-tun.errors:
|
||||
return 0, err
|
||||
default:
|
||||
buff := buff[offset-4:]
|
||||
n, err := tun.fd.Read(buff[:])
|
||||
n, err := tun.tunFile.Read(buff[:])
|
||||
if n < 4 {
|
||||
return 0, err
|
||||
}
|
||||
@@ -256,19 +258,7 @@ func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (tun *nativeTun) Read(buff []byte, offset int) (int, error) {
|
||||
for {
|
||||
n, err := tun.doRead(buff, offset)
|
||||
if err == nil || !rwcancel.ErrorIsEAGAIN(err) {
|
||||
return n, err
|
||||
}
|
||||
if !tun.rwcancel.ReadyRead() {
|
||||
return 0, errors.New("tun device closed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tun *nativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
|
||||
// reserve space for header
|
||||
|
||||
@@ -288,16 +278,20 @@ func (tun *nativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
|
||||
// write
|
||||
|
||||
return tun.fd.Write(buff)
|
||||
return tun.tunFile.Write(buff)
|
||||
}
|
||||
|
||||
func (tun *nativeTun) Close() error {
|
||||
var err3 error
|
||||
err1 := tun.rwcancel.Cancel()
|
||||
err2 := tun.fd.Close()
|
||||
func (tun *NativeTun) Flush() error {
|
||||
//TODO: can flushing be implemented by buffering and using sendmmsg?
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Close() error {
|
||||
var err2 error
|
||||
err1 := tun.tunFile.Close()
|
||||
if tun.routeSocket != -1 {
|
||||
unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR)
|
||||
err3 = unix.Close(tun.routeSocket)
|
||||
err2 = unix.Close(tun.routeSocket)
|
||||
tun.routeSocket = -1
|
||||
} else if tun.events != nil {
|
||||
close(tun.events)
|
||||
@@ -305,13 +299,10 @@ func (tun *nativeTun) Close() error {
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
return err3
|
||||
}
|
||||
|
||||
func (tun *nativeTun) setMTU(n int) error {
|
||||
func (tun *NativeTun) setMTU(n int) error {
|
||||
|
||||
// open datagram socket
|
||||
|
||||
@@ -348,7 +339,7 @@ func (tun *nativeTun) setMTU(n int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *nativeTun) MTU() (int, error) {
|
||||
func (tun *NativeTun) MTU() (int, error) {
|
||||
|
||||
// open datagram socket
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"git.zx2c4.com/wireguard-go/rwcancel"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -14,6 +13,7 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -48,16 +48,15 @@ type ifstat struct {
|
||||
Ascii [_IFSTATMAX]byte
|
||||
}
|
||||
|
||||
type nativeTun struct {
|
||||
type NativeTun struct {
|
||||
name string
|
||||
fd *os.File
|
||||
rwcancel *rwcancel.RWCancel
|
||||
tunFile *os.File
|
||||
events chan TUNEvent
|
||||
errors chan error
|
||||
routeSocket int
|
||||
}
|
||||
|
||||
func (tun *nativeTun) routineRouteListener(tunIfindex int) {
|
||||
func (tun *NativeTun) routineRouteListener(tunIfindex int) {
|
||||
var (
|
||||
statusUp bool
|
||||
statusMTU int
|
||||
@@ -67,8 +66,12 @@ func (tun *nativeTun) routineRouteListener(tunIfindex int) {
|
||||
|
||||
data := make([]byte, os.Getpagesize())
|
||||
for {
|
||||
retry:
|
||||
n, err := unix.Read(tun.routeSocket, data)
|
||||
if err != nil {
|
||||
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EINTR {
|
||||
goto retry
|
||||
}
|
||||
tun.errors <- err
|
||||
return
|
||||
}
|
||||
@@ -232,39 +235,32 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) {
|
||||
return nil, fmt.Errorf("interface %s already exists", name)
|
||||
}
|
||||
|
||||
tunfile, err := os.OpenFile("/dev/tun", unix.O_RDWR, 0)
|
||||
|
||||
tunFile, err := os.OpenFile("/dev/tun", unix.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tunfd := tunfile.Fd()
|
||||
assignedName, err := tunName(tunfd)
|
||||
|
||||
tun := NativeTun{tunFile: tunFile}
|
||||
var assignedName string
|
||||
tun.operateOnFd(func(fd uintptr) {
|
||||
assignedName, err = tunName(fd)
|
||||
})
|
||||
if err != nil {
|
||||
tunfile.Close()
|
||||
tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Enable ifhead mode, otherwise tun will complain if it gets a non-AF_INET packet
|
||||
ifheadmode := 1
|
||||
_, _, errno := unix.Syscall(
|
||||
var errno syscall.Errno
|
||||
tun.operateOnFd(func(fd uintptr) {
|
||||
_, _, errno = unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(tunfd),
|
||||
fd,
|
||||
uintptr(_TUNSIFHEAD),
|
||||
uintptr(unsafe.Pointer(&ifheadmode)),
|
||||
)
|
||||
|
||||
if errno != 0 {
|
||||
return nil, fmt.Errorf("error %s", errno.Error())
|
||||
}
|
||||
|
||||
// Set TUN iface to broadcast mode. TUN inferfaces on freebsd come up in point to point by default
|
||||
ifmodemode := unix.IFF_BROADCAST
|
||||
_, _, errno = unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(tunfd),
|
||||
uintptr(_TUNSIFMODE),
|
||||
uintptr(unsafe.Pointer(&ifmodemode)),
|
||||
)
|
||||
})
|
||||
|
||||
if errno != 0 {
|
||||
return nil, fmt.Errorf("error %s", errno.Error())
|
||||
@@ -301,25 +297,25 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) {
|
||||
uintptr(unsafe.Pointer(&ifr)),
|
||||
)
|
||||
if errno != 0 {
|
||||
tunfile.Close()
|
||||
tunFile.Close()
|
||||
tunDestroy(name)
|
||||
return nil, fmt.Errorf("failed to rename %s to %s: %s", assignedName, name, errno.Error())
|
||||
}
|
||||
|
||||
return CreateTUNFromFile(tunfile, mtu)
|
||||
return CreateTUNFromFile(tunFile, mtu)
|
||||
}
|
||||
|
||||
func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
|
||||
|
||||
tun := &nativeTun{
|
||||
fd: file,
|
||||
tun := &NativeTun{
|
||||
tunFile: file,
|
||||
events: make(chan TUNEvent, 10),
|
||||
errors: make(chan error, 1),
|
||||
}
|
||||
|
||||
name, err := tun.Name()
|
||||
if err != nil {
|
||||
tun.fd.Close()
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -331,19 +327,13 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
|
||||
return iface.Index, nil
|
||||
}()
|
||||
if err != nil {
|
||||
tun.fd.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tun.rwcancel, err = rwcancel.NewRWCancel(int(file.Fd()))
|
||||
if err != nil {
|
||||
tun.fd.Close()
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
|
||||
if err != nil {
|
||||
tun.fd.Close()
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -358,8 +348,12 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
|
||||
return tun, nil
|
||||
}
|
||||
|
||||
func (tun *nativeTun) Name() (string, error) {
|
||||
name, err := tunName(tun.fd.Fd())
|
||||
func (tun *NativeTun) Name() (string, error) {
|
||||
var name string
|
||||
var err error
|
||||
tun.operateOnFd(func(fd uintptr) {
|
||||
name, err = tunName(fd)
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -367,21 +361,21 @@ func (tun *nativeTun) Name() (string, error) {
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func (tun *nativeTun) File() *os.File {
|
||||
return tun.fd
|
||||
func (tun *NativeTun) File() *os.File {
|
||||
return tun.tunFile
|
||||
}
|
||||
|
||||
func (tun *nativeTun) Events() chan TUNEvent {
|
||||
func (tun *NativeTun) Events() chan TUNEvent {
|
||||
return tun.events
|
||||
}
|
||||
|
||||
func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) {
|
||||
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
|
||||
select {
|
||||
case err := <-tun.errors:
|
||||
return 0, err
|
||||
default:
|
||||
buff := buff[offset-4:]
|
||||
n, err := tun.fd.Read(buff[:])
|
||||
n, err := tun.tunFile.Read(buff[:])
|
||||
if n < 4 {
|
||||
return 0, err
|
||||
}
|
||||
@@ -389,19 +383,7 @@ func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (tun *nativeTun) Read(buff []byte, offset int) (int, error) {
|
||||
for {
|
||||
n, err := tun.doRead(buff, offset)
|
||||
if err == nil || !rwcancel.ErrorIsEAGAIN(err) {
|
||||
return n, err
|
||||
}
|
||||
if !tun.rwcancel.ReadyRead() {
|
||||
return 0, errors.New("tun device closed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tun *nativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
|
||||
// reserve space for header
|
||||
|
||||
@@ -421,17 +403,21 @@ func (tun *nativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
|
||||
// write
|
||||
|
||||
return tun.fd.Write(buff)
|
||||
return tun.tunFile.Write(buff)
|
||||
}
|
||||
|
||||
func (tun *nativeTun) Close() error {
|
||||
var err4 error
|
||||
err1 := tun.rwcancel.Cancel()
|
||||
err2 := tun.fd.Close()
|
||||
err3 := tunDestroy(tun.name)
|
||||
func (tun *NativeTun) Flush() error {
|
||||
//TODO: can flushing be implemented by buffering and using sendmmsg?
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Close() error {
|
||||
var err3 error
|
||||
err1 := tun.tunFile.Close()
|
||||
err2 := tunDestroy(tun.name)
|
||||
if tun.routeSocket != -1 {
|
||||
unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR)
|
||||
err4 = unix.Close(tun.routeSocket)
|
||||
err3 = unix.Close(tun.routeSocket)
|
||||
tun.routeSocket = -1
|
||||
} else if tun.events != nil {
|
||||
close(tun.events)
|
||||
@@ -442,13 +428,10 @@ func (tun *nativeTun) Close() error {
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
if err3 != nil {
|
||||
return err3
|
||||
}
|
||||
return err4
|
||||
}
|
||||
|
||||
func (tun *nativeTun) setMTU(n int) error {
|
||||
func (tun *NativeTun) setMTU(n int) error {
|
||||
// open datagram socket
|
||||
|
||||
var fd int
|
||||
@@ -485,7 +468,7 @@ func (tun *nativeTun) setMTU(n int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *nativeTun) MTU() (int, error) {
|
||||
func (tun *NativeTun) MTU() (int, error) {
|
||||
// open datagram socket
|
||||
|
||||
fd, err := unix.Socket(
|
||||
|
||||
176
tun/tun_linux.go
176
tun/tun_linux.go
@@ -1,27 +1,24 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Mathias N. Hall-Andersen <mathias@hall-andersen.dk>.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/* Copyright 2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
|
||||
|
||||
package tun
|
||||
|
||||
/* Implementation of the TUN device interface for linux
|
||||
*/
|
||||
|
||||
import (
|
||||
"git.zx2c4.com/wireguard-go/rwcancel"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"golang.org/x/net/ipv6"
|
||||
"golang.org/x/sys/unix"
|
||||
"golang.zx2c4.com/wireguard/rwcancel"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -31,9 +28,8 @@ const (
|
||||
ifReqSize = unix.IFNAMSIZ + 64
|
||||
)
|
||||
|
||||
type nativeTun struct {
|
||||
fd *os.File
|
||||
fdCancel *rwcancel.RWCancel
|
||||
type NativeTun struct {
|
||||
tunFile *os.File
|
||||
index int32 // if index
|
||||
name string // name of interface
|
||||
errors chan error // async error handling
|
||||
@@ -45,18 +41,26 @@ type nativeTun struct {
|
||||
statusListenersShutdown chan struct{}
|
||||
}
|
||||
|
||||
func (tun *nativeTun) File() *os.File {
|
||||
return tun.fd
|
||||
func (tun *NativeTun) File() *os.File {
|
||||
return tun.tunFile
|
||||
}
|
||||
|
||||
func (tun *nativeTun) routineHackListener() {
|
||||
func (tun *NativeTun) routineHackListener() {
|
||||
defer tun.hackListenerClosed.Unlock()
|
||||
/* This is needed for the detection to work across network namespaces
|
||||
* If you are reading this and know a better method, please get in touch.
|
||||
*/
|
||||
fd := int(tun.fd.Fd())
|
||||
for {
|
||||
_, err := unix.Write(fd, nil)
|
||||
sysconn, err := tun.tunFile.SyscallConn()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err2 := sysconn.Control(func(fd uintptr) {
|
||||
_, err = unix.Write(int(fd), nil)
|
||||
})
|
||||
if err2 != nil {
|
||||
return
|
||||
}
|
||||
switch err {
|
||||
case unix.EINVAL:
|
||||
tun.events <- TUNEventUp
|
||||
@@ -89,7 +93,7 @@ func createNetlinkSocket() (int, error) {
|
||||
return sock, nil
|
||||
}
|
||||
|
||||
func (tun *nativeTun) routineNetlinkListener() {
|
||||
func (tun *NativeTun) routineNetlinkListener() {
|
||||
defer func() {
|
||||
unix.Close(tun.netlinkSock)
|
||||
tun.hackListenerClosed.Lock()
|
||||
@@ -102,7 +106,7 @@ func (tun *nativeTun) routineNetlinkListener() {
|
||||
var msgn int
|
||||
for {
|
||||
msgn, _, _, _, err = unix.Recvmsg(tun.netlinkSock, msg[:], nil, 0)
|
||||
if err == nil || !rwcancel.ErrorIsEAGAIN(err) {
|
||||
if err == nil || !rwcancel.RetryAfterError(err) {
|
||||
break
|
||||
}
|
||||
if !tun.netlinkCancel.ReadyRead() {
|
||||
@@ -159,21 +163,17 @@ func (tun *nativeTun) routineNetlinkListener() {
|
||||
}
|
||||
}
|
||||
|
||||
func (tun *nativeTun) isUp() (bool, error) {
|
||||
func (tun *NativeTun) isUp() (bool, error) {
|
||||
inter, err := net.InterfaceByName(tun.name)
|
||||
return inter.Flags&net.FlagUp != 0, err
|
||||
}
|
||||
|
||||
func getDummySock() (int, error) {
|
||||
return unix.Socket(
|
||||
func getIFIndex(name string) (int32, error) {
|
||||
fd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
func getIFIndex(name string) (int32, error) {
|
||||
fd, err := getDummySock()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -196,10 +196,8 @@ func getIFIndex(name string) (int32, error) {
|
||||
return *(*int32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])), nil
|
||||
}
|
||||
|
||||
func (tun *nativeTun) setMTU(n int) error {
|
||||
|
||||
func (tun *NativeTun) setMTU(n int) error {
|
||||
// open datagram socket
|
||||
|
||||
fd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
@@ -231,10 +229,8 @@ func (tun *nativeTun) setMTU(n int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *nativeTun) MTU() (int, error) {
|
||||
|
||||
func (tun *NativeTun) MTU() (int, error) {
|
||||
// open datagram socket
|
||||
|
||||
fd, err := unix.Socket(
|
||||
unix.AF_INET,
|
||||
unix.SOCK_DGRAM,
|
||||
@@ -258,23 +254,32 @@ func (tun *nativeTun) MTU() (int, error) {
|
||||
uintptr(unsafe.Pointer(&ifr[0])),
|
||||
)
|
||||
if errno != 0 {
|
||||
return 0, errors.New("failed to get MTU of TUN device: " + strconv.FormatInt(int64(errno), 10))
|
||||
return 0, errors.New("failed to get MTU of TUN device: " + errno.Error())
|
||||
}
|
||||
|
||||
return int(*(*int32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ]))), nil
|
||||
}
|
||||
|
||||
func (tun *nativeTun) Name() (string, error) {
|
||||
|
||||
func (tun *NativeTun) Name() (string, error) {
|
||||
sysconn, err := tun.tunFile.SyscallConn()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var ifr [ifReqSize]byte
|
||||
_, _, errno := unix.Syscall(
|
||||
var errno syscall.Errno
|
||||
err = sysconn.Control(func(fd uintptr) {
|
||||
_, _, errno = unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
tun.fd.Fd(),
|
||||
fd,
|
||||
uintptr(unix.TUNGETIFF),
|
||||
uintptr(unsafe.Pointer(&ifr[0])),
|
||||
)
|
||||
})
|
||||
if err != nil {
|
||||
return "", errors.New("failed to get name of TUN device: " + err.Error())
|
||||
}
|
||||
if errno != 0 {
|
||||
return "", errors.New("failed to get name of TUN device: " + strconv.FormatInt(int64(errno), 10))
|
||||
return "", errors.New("failed to get name of TUN device: " + errno.Error())
|
||||
}
|
||||
nullStr := ifr[:]
|
||||
i := bytes.IndexByte(nullStr, 0)
|
||||
@@ -285,7 +290,7 @@ func (tun *nativeTun) Name() (string, error) {
|
||||
return tun.name, nil
|
||||
}
|
||||
|
||||
func (tun *nativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
|
||||
if tun.nopi {
|
||||
buff = buff[offset:]
|
||||
@@ -310,19 +315,24 @@ func (tun *nativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
|
||||
// write
|
||||
|
||||
return tun.fd.Write(buff)
|
||||
return tun.tunFile.Write(buff)
|
||||
}
|
||||
|
||||
func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) {
|
||||
func (tun *NativeTun) Flush() error {
|
||||
//TODO: can flushing be implemented by buffering and using sendmmsg?
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
|
||||
select {
|
||||
case err := <-tun.errors:
|
||||
return 0, err
|
||||
default:
|
||||
if tun.nopi {
|
||||
return tun.fd.Read(buff[offset:])
|
||||
return tun.tunFile.Read(buff[offset:])
|
||||
} else {
|
||||
buff := buff[offset-4:]
|
||||
n, err := tun.fd.Read(buff[:])
|
||||
n, err := tun.tunFile.Read(buff[:])
|
||||
if n < 4 {
|
||||
return 0, err
|
||||
}
|
||||
@@ -331,23 +341,11 @@ func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (tun *nativeTun) Read(buff []byte, offset int) (int, error) {
|
||||
for {
|
||||
n, err := tun.doRead(buff, offset)
|
||||
if err == nil || !rwcancel.ErrorIsEAGAIN(err) {
|
||||
return n, err
|
||||
}
|
||||
if !tun.fdCancel.ReadyRead() {
|
||||
return 0, errors.New("tun device closed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tun *nativeTun) Events() chan TUNEvent {
|
||||
func (tun *NativeTun) Events() chan TUNEvent {
|
||||
return tun.events
|
||||
}
|
||||
|
||||
func (tun *nativeTun) Close() error {
|
||||
func (tun *NativeTun) Close() error {
|
||||
var err1 error
|
||||
if tun.statusListenersShutdown != nil {
|
||||
close(tun.statusListenersShutdown)
|
||||
@@ -357,41 +355,20 @@ func (tun *nativeTun) Close() error {
|
||||
} else if tun.events != nil {
|
||||
close(tun.events)
|
||||
}
|
||||
err2 := tun.fd.Close()
|
||||
err3 := tun.fdCancel.Cancel()
|
||||
err2 := tun.tunFile.Close()
|
||||
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
return err3
|
||||
}
|
||||
|
||||
func CreateTUN(name string, mtu int) (TUNDevice, error) {
|
||||
|
||||
// open clone device
|
||||
|
||||
// HACK: we open it as a raw Fd first, so that f.nonblock=false
|
||||
// when we make it into a file object.
|
||||
nfd, err := unix.Open(cloneDevicePath, os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = unix.SetNonblock(nfd, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fd := os.NewFile(uintptr(nfd), cloneDevicePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// create new device
|
||||
|
||||
var ifr [ifReqSize]byte
|
||||
var flags uint16 = unix.IFF_TUN // | unix.IFF_NO_PI (disabled for TUN status hack)
|
||||
nameBytes := []byte(name)
|
||||
@@ -403,20 +380,28 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) {
|
||||
|
||||
_, _, errno := unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
fd.Fd(),
|
||||
uintptr(nfd),
|
||||
uintptr(unix.TUNSETIFF),
|
||||
uintptr(unsafe.Pointer(&ifr[0])),
|
||||
)
|
||||
if errno != 0 {
|
||||
return nil, errno
|
||||
}
|
||||
err = unix.SetNonblock(nfd, true)
|
||||
|
||||
// Note that the above -- open,ioctl,nonblock -- must happen prior to handing it to netpoll as below this line.
|
||||
|
||||
fd := os.NewFile(uintptr(nfd), cloneDevicePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return CreateTUNFromFile(fd, mtu)
|
||||
}
|
||||
|
||||
func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
|
||||
tun := &nativeTun{
|
||||
fd: file,
|
||||
tun := &NativeTun{
|
||||
tunFile: file,
|
||||
events: make(chan TUNEvent, 5),
|
||||
errors: make(chan error, 5),
|
||||
statusListenersShutdown: make(chan struct{}),
|
||||
@@ -424,15 +409,8 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
|
||||
}
|
||||
var err error
|
||||
|
||||
tun.fdCancel, err = rwcancel.NewRWCancel(int(file.Fd()))
|
||||
if err != nil {
|
||||
tun.fd.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = tun.Name()
|
||||
if err != nil {
|
||||
tun.fd.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -445,12 +423,11 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
|
||||
|
||||
tun.netlinkSock, err = createNetlinkSocket()
|
||||
if err != nil {
|
||||
tun.fd.Close()
|
||||
return nil, err
|
||||
}
|
||||
tun.netlinkCancel, err = rwcancel.NewRWCancel(tun.netlinkSock)
|
||||
if err != nil {
|
||||
tun.fd.Close()
|
||||
unix.Close(tun.netlinkSock)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -460,9 +437,28 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
|
||||
|
||||
err = tun.setMTU(mtu)
|
||||
if err != nil {
|
||||
tun.Close()
|
||||
unix.Close(tun.netlinkSock)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tun, nil
|
||||
}
|
||||
|
||||
func CreateUnmonitoredTUNFromFD(fd int) (TUNDevice, string, error) {
|
||||
err := unix.SetNonblock(fd, true)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
file := os.NewFile(uintptr(fd), "/dev/tun")
|
||||
tun := &NativeTun{
|
||||
tunFile: file,
|
||||
events: make(chan TUNEvent, 5),
|
||||
errors: make(chan error, 5),
|
||||
nopi: true,
|
||||
}
|
||||
name, err := tun.Name()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return tun, name, nil
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
* Copyright (C) 2017-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"git.zx2c4.com/wireguard-go/rwcancel"
|
||||
"errors"
|
||||
"fmt"
|
||||
"golang.org/x/net/ipv6"
|
||||
"golang.org/x/sys/unix"
|
||||
@@ -27,16 +25,15 @@ type ifreq_mtu struct {
|
||||
|
||||
const _TUNSIFMODE = 0x8004745d
|
||||
|
||||
type nativeTun struct {
|
||||
type NativeTun struct {
|
||||
name string
|
||||
fd *os.File
|
||||
rwcancel *rwcancel.RWCancel
|
||||
tunFile *os.File
|
||||
events chan TUNEvent
|
||||
errors chan error
|
||||
routeSocket int
|
||||
}
|
||||
|
||||
func (tun *nativeTun) routineRouteListener(tunIfindex int) {
|
||||
func (tun *NativeTun) routineRouteListener(tunIfindex int) {
|
||||
var (
|
||||
statusUp bool
|
||||
statusMTU int
|
||||
@@ -46,8 +43,12 @@ func (tun *nativeTun) routineRouteListener(tunIfindex int) {
|
||||
|
||||
data := make([]byte, os.Getpagesize())
|
||||
for {
|
||||
retry:
|
||||
n, err := unix.Read(tun.routeSocket, data)
|
||||
if err != nil {
|
||||
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EINTR {
|
||||
goto retry
|
||||
}
|
||||
tun.errors <- err
|
||||
return
|
||||
}
|
||||
@@ -90,9 +91,7 @@ func (tun *nativeTun) routineRouteListener(tunIfindex int) {
|
||||
|
||||
func errorIsEBUSY(err error) bool {
|
||||
if pe, ok := err.(*os.PathError); ok {
|
||||
if errno, ok := pe.Err.(syscall.Errno); ok && errno == syscall.EBUSY {
|
||||
return true
|
||||
}
|
||||
err = pe.Err
|
||||
}
|
||||
if errno, ok := err.(syscall.Errno); ok && errno == syscall.EBUSY {
|
||||
return true
|
||||
@@ -127,25 +126,12 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set TUN iface to broadcast mode
|
||||
ifmodemode := unix.IFF_BROADCAST
|
||||
_, _, errno := unix.Syscall(
|
||||
unix.SYS_IOCTL,
|
||||
uintptr(tunfile.Fd()),
|
||||
uintptr(_TUNSIFMODE),
|
||||
uintptr(unsafe.Pointer(&ifmodemode)),
|
||||
)
|
||||
|
||||
if errno != 0 {
|
||||
return nil, fmt.Errorf("error %s", errno.Error())
|
||||
}
|
||||
|
||||
tun, err := CreateTUNFromFile(tunfile, mtu)
|
||||
|
||||
if err == nil && name == "tun" {
|
||||
fname := os.Getenv("WG_TUN_NAME_FILE")
|
||||
if fname != "" {
|
||||
ioutil.WriteFile(fname, []byte(tun.(*nativeTun).name+"\n"), 0400)
|
||||
ioutil.WriteFile(fname, []byte(tun.(*NativeTun).name+"\n"), 0400)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,15 +140,15 @@ func CreateTUN(name string, mtu int) (TUNDevice, error) {
|
||||
|
||||
func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
|
||||
|
||||
tun := &nativeTun{
|
||||
fd: file,
|
||||
tun := &NativeTun{
|
||||
tunFile: file,
|
||||
events: make(chan TUNEvent, 10),
|
||||
errors: make(chan error, 1),
|
||||
}
|
||||
|
||||
name, err := tun.Name()
|
||||
if err != nil {
|
||||
tun.fd.Close()
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -174,19 +160,13 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
|
||||
return iface.Index, nil
|
||||
}()
|
||||
if err != nil {
|
||||
tun.fd.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tun.rwcancel, err = rwcancel.NewRWCancel(int(file.Fd()))
|
||||
if err != nil {
|
||||
tun.fd.Close()
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tun.routeSocket, err = unix.Socket(unix.AF_ROUTE, unix.SOCK_RAW, unix.AF_UNSPEC)
|
||||
if err != nil {
|
||||
tun.fd.Close()
|
||||
tun.tunFile.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -201,8 +181,8 @@ func CreateTUNFromFile(file *os.File, mtu int) (TUNDevice, error) {
|
||||
return tun, nil
|
||||
}
|
||||
|
||||
func (tun *nativeTun) Name() (string, error) {
|
||||
gostat, err := tun.fd.Stat()
|
||||
func (tun *NativeTun) Name() (string, error) {
|
||||
gostat, err := tun.tunFile.Stat()
|
||||
if err != nil {
|
||||
tun.name = ""
|
||||
return "", err
|
||||
@@ -212,21 +192,21 @@ func (tun *nativeTun) Name() (string, error) {
|
||||
return tun.name, nil
|
||||
}
|
||||
|
||||
func (tun *nativeTun) File() *os.File {
|
||||
return tun.fd
|
||||
func (tun *NativeTun) File() *os.File {
|
||||
return tun.tunFile
|
||||
}
|
||||
|
||||
func (tun *nativeTun) Events() chan TUNEvent {
|
||||
func (tun *NativeTun) Events() chan TUNEvent {
|
||||
return tun.events
|
||||
}
|
||||
|
||||
func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) {
|
||||
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
|
||||
select {
|
||||
case err := <-tun.errors:
|
||||
return 0, err
|
||||
default:
|
||||
buff := buff[offset-4:]
|
||||
n, err := tun.fd.Read(buff[:])
|
||||
n, err := tun.tunFile.Read(buff[:])
|
||||
if n < 4 {
|
||||
return 0, err
|
||||
}
|
||||
@@ -234,19 +214,7 @@ func (tun *nativeTun) doRead(buff []byte, offset int) (int, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (tun *nativeTun) Read(buff []byte, offset int) (int, error) {
|
||||
for {
|
||||
n, err := tun.doRead(buff, offset)
|
||||
if err == nil || !rwcancel.ErrorIsEAGAIN(err) {
|
||||
return n, err
|
||||
}
|
||||
if !tun.rwcancel.ReadyRead() {
|
||||
return 0, errors.New("tun device closed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tun *nativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
|
||||
// reserve space for header
|
||||
|
||||
@@ -266,16 +234,20 @@ func (tun *nativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
|
||||
// write
|
||||
|
||||
return tun.fd.Write(buff)
|
||||
return tun.tunFile.Write(buff)
|
||||
}
|
||||
|
||||
func (tun *nativeTun) Close() error {
|
||||
var err3 error
|
||||
err1 := tun.rwcancel.Cancel()
|
||||
err2 := tun.fd.Close()
|
||||
func (tun *NativeTun) Flush() error {
|
||||
//TODO: can flushing be implemented by buffering and using sendmmsg?
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Close() error {
|
||||
var err2 error
|
||||
err1 := tun.tunFile.Close()
|
||||
if tun.routeSocket != -1 {
|
||||
unix.Shutdown(tun.routeSocket, unix.SHUT_RDWR)
|
||||
err3 = unix.Close(tun.routeSocket)
|
||||
err2 = unix.Close(tun.routeSocket)
|
||||
tun.routeSocket = -1
|
||||
} else if tun.events != nil {
|
||||
close(tun.events)
|
||||
@@ -283,13 +255,10 @@ func (tun *nativeTun) Close() error {
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
return err3
|
||||
}
|
||||
|
||||
func (tun *nativeTun) setMTU(n int) error {
|
||||
func (tun *NativeTun) setMTU(n int) error {
|
||||
// open datagram socket
|
||||
|
||||
var fd int
|
||||
@@ -326,7 +295,7 @@ func (tun *nativeTun) setMTU(n int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *nativeTun) MTU() (int, error) {
|
||||
func (tun *NativeTun) MTU() (int, error) {
|
||||
// open datagram socket
|
||||
|
||||
fd, err := unix.Socket(
|
||||
|
||||
373
tun/tun_windows.go
Normal file
373
tun/tun_windows.go
Normal file
@@ -0,0 +1,373 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2018-2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package tun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.zx2c4.com/wireguard/tun/wintun"
|
||||
)
|
||||
|
||||
const (
|
||||
packetExchangeMax uint32 = 256 // Number of packets that may be written at a time
|
||||
packetExchangeAlignment uint32 = 16 // Number of bytes packets are aligned to in exchange buffers
|
||||
packetSizeMax uint32 = 0xf000 - packetExchangeAlignment // Maximum packet size
|
||||
packetExchangeSize uint32 = 0x100000 // Exchange buffer size (defaults to 1MiB)
|
||||
retryRate = 4 // Number of retries per second to reopen device pipe
|
||||
retryTimeout = 30 // Number of seconds to tolerate adapter unavailable
|
||||
)
|
||||
|
||||
type exchgBufRead struct {
|
||||
data [packetExchangeSize]byte
|
||||
offset uint32
|
||||
avail uint32
|
||||
}
|
||||
|
||||
type exchgBufWrite struct {
|
||||
data [packetExchangeSize]byte
|
||||
offset uint32
|
||||
packetNum uint32
|
||||
}
|
||||
|
||||
type NativeTun struct {
|
||||
wt *wintun.Wintun
|
||||
tunFileRead *os.File
|
||||
tunFileWrite *os.File
|
||||
tunLock sync.Mutex
|
||||
close bool
|
||||
rdBuff *exchgBufRead
|
||||
wrBuff *exchgBufWrite
|
||||
events chan TUNEvent
|
||||
errors chan error
|
||||
forcedMtu int
|
||||
}
|
||||
|
||||
func packetAlign(size uint32) uint32 {
|
||||
return (size + (packetExchangeAlignment - 1)) &^ (packetExchangeAlignment - 1)
|
||||
}
|
||||
|
||||
//
|
||||
// CreateTUN creates a Wintun adapter with the given name. Should a Wintun
|
||||
// adapter with the same name exist, it is reused.
|
||||
//
|
||||
func CreateTUN(ifname string) (TUNDevice, error) {
|
||||
// Does an interface with this name already exist?
|
||||
wt, err := wintun.GetInterface(ifname, 0)
|
||||
if wt == nil {
|
||||
// Interface does not exist or an error occured. Create one.
|
||||
wt, _, err = wintun.CreateInterface("WireGuard Tunnel Adapter", 0)
|
||||
if err != nil {
|
||||
return nil, errors.New("Creating Wintun adapter failed: " + err.Error())
|
||||
}
|
||||
} else if err != nil {
|
||||
// Foreign interface with the same name found.
|
||||
// We could create a Wintun interface under a temporary name. But, should our
|
||||
// proces die without deleting this interface first, the interface would remain
|
||||
// orphaned.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = wt.SetInterfaceName(ifname)
|
||||
if err != nil {
|
||||
wt.DeleteInterface(0)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = wt.FlushInterface()
|
||||
if err != nil {
|
||||
wt.DeleteInterface(0)
|
||||
return nil, errors.New("Flushing interface failed: " + err.Error())
|
||||
}
|
||||
|
||||
return &NativeTun{
|
||||
wt: wt,
|
||||
rdBuff: &exchgBufRead{},
|
||||
wrBuff: &exchgBufWrite{},
|
||||
events: make(chan TUNEvent, 10),
|
||||
errors: make(chan error, 1),
|
||||
forcedMtu: 1500,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) openTUN() error {
|
||||
retries := retryTimeout * retryRate
|
||||
if tun.close {
|
||||
return os.ErrClosed
|
||||
}
|
||||
|
||||
var err error
|
||||
name := tun.wt.DataFileName()
|
||||
for tun.tunFileRead == nil {
|
||||
tun.tunFileRead, err = os.OpenFile(name, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
if retries > 0 && !tun.close {
|
||||
time.Sleep(time.Second / retryRate)
|
||||
retries--
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
for tun.tunFileWrite == nil {
|
||||
tun.tunFileWrite, err = os.OpenFile(name, os.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
if retries > 0 && !tun.close {
|
||||
time.Sleep(time.Second / retryRate)
|
||||
retries--
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) closeTUN() (err error) {
|
||||
for tun.tunFileRead != nil {
|
||||
tun.tunLock.Lock()
|
||||
if tun.tunFileRead == nil {
|
||||
tun.tunLock.Unlock()
|
||||
break
|
||||
}
|
||||
t := tun.tunFileRead
|
||||
tun.tunFileRead = nil
|
||||
windows.CancelIoEx(windows.Handle(t.Fd()), nil)
|
||||
err = t.Close()
|
||||
tun.tunLock.Unlock()
|
||||
break
|
||||
}
|
||||
for tun.tunFileWrite != nil {
|
||||
tun.tunLock.Lock()
|
||||
if tun.tunFileWrite == nil {
|
||||
tun.tunLock.Unlock()
|
||||
break
|
||||
}
|
||||
t := tun.tunFileWrite
|
||||
tun.tunFileWrite = nil
|
||||
windows.CancelIoEx(windows.Handle(t.Fd()), nil)
|
||||
err2 := t.Close()
|
||||
tun.tunLock.Unlock()
|
||||
if err == nil {
|
||||
err = err2
|
||||
}
|
||||
break
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (tun *NativeTun) getTUN() (read *os.File, write *os.File, err error) {
|
||||
read, write = tun.tunFileRead, tun.tunFileWrite
|
||||
if read == nil || write == nil {
|
||||
read, write = nil, nil
|
||||
tun.tunLock.Lock()
|
||||
if tun.tunFileRead != nil && tun.tunFileWrite != nil {
|
||||
read, write = tun.tunFileRead, tun.tunFileWrite
|
||||
tun.tunLock.Unlock()
|
||||
return
|
||||
}
|
||||
err = tun.closeTUN()
|
||||
if err != nil {
|
||||
tun.tunLock.Unlock()
|
||||
return
|
||||
}
|
||||
err = tun.openTUN()
|
||||
if err == nil {
|
||||
read, write = tun.tunFileRead, tun.tunFileWrite
|
||||
}
|
||||
tun.tunLock.Unlock()
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Name() (string, error) {
|
||||
return tun.wt.GetInterfaceName()
|
||||
}
|
||||
|
||||
func (tun *NativeTun) File() *os.File {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Events() chan TUNEvent {
|
||||
return tun.events
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Close() error {
|
||||
tun.close = true
|
||||
err1 := tun.closeTUN()
|
||||
|
||||
if tun.events != nil {
|
||||
close(tun.events)
|
||||
}
|
||||
|
||||
_, _, err2 := tun.wt.DeleteInterface(0)
|
||||
if err1 == nil {
|
||||
err1 = err2
|
||||
}
|
||||
|
||||
return err1
|
||||
}
|
||||
|
||||
func (tun *NativeTun) MTU() (int, error) {
|
||||
return tun.forcedMtu, nil
|
||||
}
|
||||
|
||||
//TODO: This is a temporary hack. We really need to be monitoring the interface in real time and adapting to MTU changes.
|
||||
func (tun *NativeTun) ForceMtu(mtu int) {
|
||||
tun.forcedMtu = mtu
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Read(buff []byte, offset int) (int, error) {
|
||||
select {
|
||||
case err := <-tun.errors:
|
||||
return 0, err
|
||||
default:
|
||||
}
|
||||
|
||||
for {
|
||||
if tun.rdBuff.offset+packetExchangeAlignment <= tun.rdBuff.avail {
|
||||
// Get packet from the exchange buffer.
|
||||
packet := tun.rdBuff.data[tun.rdBuff.offset:]
|
||||
size := *(*uint32)(unsafe.Pointer(&packet[0]))
|
||||
pSize := packetAlign(packetExchangeAlignment + size)
|
||||
if packetSizeMax < size || tun.rdBuff.avail < tun.rdBuff.offset+pSize {
|
||||
// Invalid packet size.
|
||||
tun.rdBuff.avail = 0
|
||||
continue
|
||||
}
|
||||
packet = packet[packetExchangeAlignment : packetExchangeAlignment+size]
|
||||
|
||||
// Copy data.
|
||||
copy(buff[offset:], packet)
|
||||
tun.rdBuff.offset += pSize
|
||||
return int(size), nil
|
||||
}
|
||||
|
||||
// Get TUN data pipe.
|
||||
file, _, err := tun.getTUN()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Fill queue.
|
||||
retries := 1000
|
||||
for {
|
||||
n, err := file.Read(tun.rdBuff.data[:])
|
||||
if err != nil {
|
||||
tun.rdBuff.offset = 0
|
||||
tun.rdBuff.avail = 0
|
||||
pe, ok := err.(*os.PathError)
|
||||
if tun.close {
|
||||
return 0, os.ErrClosed
|
||||
}
|
||||
if retries > 0 && ok && pe.Err == windows.ERROR_OPERATION_ABORTED {
|
||||
retries--
|
||||
continue
|
||||
}
|
||||
if ok && pe.Err == syscall.Errno(6) /*windows.ERROR_INVALID_HANDLE*/ {
|
||||
tun.closeTUN()
|
||||
break
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
tun.rdBuff.offset = 0
|
||||
tun.rdBuff.avail = uint32(n)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note: flush() and putTunPacket() assume the caller comes only from a single thread; there's no locking.
|
||||
|
||||
func (tun *NativeTun) Flush() error {
|
||||
if tun.wrBuff.offset == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for {
|
||||
// Get TUN data pipe.
|
||||
_, file, err := tun.getTUN()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Flush write buffer.
|
||||
retries := retryTimeout * retryRate
|
||||
for {
|
||||
_, err = file.Write(tun.wrBuff.data[:tun.wrBuff.offset])
|
||||
tun.wrBuff.packetNum = 0
|
||||
tun.wrBuff.offset = 0
|
||||
if err != nil {
|
||||
pe, ok := err.(*os.PathError)
|
||||
if tun.close {
|
||||
return os.ErrClosed
|
||||
}
|
||||
if retries > 0 && ok && pe.Err == windows.ERROR_OPERATION_ABORTED {
|
||||
time.Sleep(time.Second / retryRate)
|
||||
retries--
|
||||
continue
|
||||
}
|
||||
if ok && pe.Err == syscall.Errno(6) /*windows.ERROR_INVALID_HANDLE*/ {
|
||||
tun.closeTUN()
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tun *NativeTun) putTunPacket(buff []byte) error {
|
||||
size := uint32(len(buff))
|
||||
if size == 0 {
|
||||
return errors.New("Empty packet")
|
||||
}
|
||||
if size > packetSizeMax {
|
||||
return errors.New("Packet too big")
|
||||
}
|
||||
pSize := packetAlign(packetExchangeAlignment + size)
|
||||
|
||||
if tun.wrBuff.packetNum >= packetExchangeMax || tun.wrBuff.offset+pSize >= packetExchangeSize {
|
||||
// Exchange buffer is full -> flush first.
|
||||
err := tun.Flush()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Write packet to the exchange buffer.
|
||||
packet := tun.wrBuff.data[tun.wrBuff.offset : tun.wrBuff.offset+pSize]
|
||||
*(*uint32)(unsafe.Pointer(&packet[0])) = size
|
||||
packet = packet[packetExchangeAlignment : packetExchangeAlignment+size]
|
||||
copy(packet, buff)
|
||||
|
||||
tun.wrBuff.packetNum++
|
||||
tun.wrBuff.offset += pSize
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tun *NativeTun) Write(buff []byte, offset int) (int, error) {
|
||||
err := tun.putTunPacket(buff[offset:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(buff) - offset, nil
|
||||
}
|
||||
|
||||
//
|
||||
// GUID returns Windows adapter instance ID.
|
||||
//
|
||||
func (tun *NativeTun) GUID() windows.GUID {
|
||||
return tun.wt.CfgInstanceID
|
||||
}
|
||||
44
tun/wintun/guid/guid_windows.go
Normal file
44
tun/wintun/guid/guid_windows.go
Normal file
@@ -0,0 +1,44 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package guid
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
//sys clsidFromString(lpsz *uint16, pclsid *windows.GUID) (hr int32) = ole32.CLSIDFromString
|
||||
|
||||
//
|
||||
// FromString parses "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}" string to GUID.
|
||||
//
|
||||
func FromString(str string) (*windows.GUID, error) {
|
||||
strUTF16, err := syscall.UTF16PtrFromString(str)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
guid := &windows.GUID{}
|
||||
|
||||
hr := clsidFromString(strUTF16, guid)
|
||||
if hr < 0 {
|
||||
return nil, syscall.Errno(hr)
|
||||
}
|
||||
|
||||
return guid, nil
|
||||
}
|
||||
|
||||
//
|
||||
// ToString function converts GUID to string
|
||||
// "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}".
|
||||
//
|
||||
// The resulting string is uppercase.
|
||||
//
|
||||
func ToString(guid *windows.GUID) string {
|
||||
return fmt.Sprintf("{%06X-%04X-%04X-%04X-%012X}", guid.Data1, guid.Data2, guid.Data3, guid.Data4[:2], guid.Data4[2:])
|
||||
}
|
||||
8
tun/wintun/guid/mksyscall.go
Normal file
8
tun/wintun/guid/mksyscall.go
Normal file
@@ -0,0 +1,8 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package guid
|
||||
|
||||
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zguid_windows.go guid_windows.go
|
||||
49
tun/wintun/guid/zguid_windows.go
Normal file
49
tun/wintun/guid/zguid_windows.go
Normal file
@@ -0,0 +1,49 @@
|
||||
// Code generated by 'go generate'; DO NOT EDIT.
|
||||
|
||||
package guid
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
// Do the interface allocations only once for common
|
||||
// Errno values.
|
||||
const (
|
||||
errnoERROR_IO_PENDING = 997
|
||||
)
|
||||
|
||||
var (
|
||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||
)
|
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e syscall.Errno) error {
|
||||
switch e {
|
||||
case 0:
|
||||
return nil
|
||||
case errnoERROR_IO_PENDING:
|
||||
return errERROR_IO_PENDING
|
||||
}
|
||||
// TODO: add more here, after collecting data on the common
|
||||
// error values see on Windows. (perhaps when running
|
||||
// all.bat?)
|
||||
return e
|
||||
}
|
||||
|
||||
var (
|
||||
modole32 = windows.NewLazySystemDLL("ole32.dll")
|
||||
|
||||
procCLSIDFromString = modole32.NewProc("CLSIDFromString")
|
||||
)
|
||||
|
||||
func clsidFromString(lpsz *uint16, pclsid *windows.GUID) (hr int32) {
|
||||
r0, _, _ := syscall.Syscall(procCLSIDFromString.Addr(), 2, uintptr(unsafe.Pointer(lpsz)), uintptr(unsafe.Pointer(pclsid)), 0)
|
||||
hr = int32(r0)
|
||||
return
|
||||
}
|
||||
32
tun/wintun/netshell/netshell_windows.go
Normal file
32
tun/wintun/netshell/netshell_windows.go
Normal file
@@ -0,0 +1,32 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package netshell
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
modnetshell = windows.NewLazySystemDLL("netshell.dll")
|
||||
procHrRenameConnection = modnetshell.NewProc("HrRenameConnection")
|
||||
)
|
||||
|
||||
func HrRenameConnection(guid *windows.GUID, newName *uint16) (err error) {
|
||||
err = procHrRenameConnection.Find()
|
||||
if err != nil {
|
||||
// Missing from servercore, so we can't presume it's always there.
|
||||
return
|
||||
}
|
||||
|
||||
ret, _, _ := syscall.Syscall(procHrRenameConnection.Addr(), 2, uintptr(unsafe.Pointer(guid)), uintptr(unsafe.Pointer(newName)), 0)
|
||||
if ret != 0 {
|
||||
err = syscall.Errno(ret)
|
||||
}
|
||||
return
|
||||
}
|
||||
42
tun/wintun/registryhacks_windows.go
Normal file
42
tun/wintun/registryhacks_windows.go
Normal file
@@ -0,0 +1,42 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package wintun
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows/registry"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
numRetries = 25
|
||||
retryTimeout = 100 * time.Millisecond
|
||||
)
|
||||
|
||||
func registryOpenKeyRetry(k registry.Key, path string, access uint32) (key registry.Key, err error) {
|
||||
for i := 0; i < numRetries; i++ {
|
||||
key, err = registry.OpenKey(k, path, access)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if i != numRetries - 1 {
|
||||
time.Sleep(retryTimeout)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func keyGetStringValueRetry(k registry.Key, name string) (val string, valtype uint32, err error) {
|
||||
for i := 0; i < numRetries; i++ {
|
||||
val, valtype, err = k.GetStringValue(name)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if i != numRetries - 1 {
|
||||
time.Sleep(retryTimeout)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
8
tun/wintun/setupapi/mksyscall.go
Normal file
8
tun/wintun/setupapi/mksyscall.go
Normal file
@@ -0,0 +1,8 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package setupapi
|
||||
|
||||
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsetupapi_windows.go setupapi_windows.go
|
||||
458
tun/wintun/setupapi/setupapi_windows.go
Normal file
458
tun/wintun/setupapi/setupapi_windows.go
Normal file
@@ -0,0 +1,458 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package setupapi
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
//sys setupDiCreateDeviceInfoListEx(classGUID *windows.GUID, hwndParent uintptr, machineName *uint16, reserved uintptr) (handle DevInfo, err error) [failretval==DevInfo(windows.InvalidHandle)] = setupapi.SetupDiCreateDeviceInfoListExW
|
||||
|
||||
// SetupDiCreateDeviceInfoListEx function creates an empty device information set on a remote or a local computer and optionally associates the set with a device setup class.
|
||||
func SetupDiCreateDeviceInfoListEx(classGUID *windows.GUID, hwndParent uintptr, machineName string) (deviceInfoSet DevInfo, err error) {
|
||||
var machineNameUTF16 *uint16
|
||||
if machineName != "" {
|
||||
machineNameUTF16, err = syscall.UTF16PtrFromString(machineName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return setupDiCreateDeviceInfoListEx(classGUID, hwndParent, machineNameUTF16, 0)
|
||||
}
|
||||
|
||||
//sys setupDiGetDeviceInfoListDetail(deviceInfoSet DevInfo, deviceInfoSetDetailData *DevInfoListDetailData) (err error) = setupapi.SetupDiGetDeviceInfoListDetailW
|
||||
|
||||
// SetupDiGetDeviceInfoListDetail function retrieves information associated with a device information set including the class GUID, remote computer handle, and remote computer name.
|
||||
func SetupDiGetDeviceInfoListDetail(deviceInfoSet DevInfo) (deviceInfoSetDetailData *DevInfoListDetailData, err error) {
|
||||
data := &DevInfoListDetailData{}
|
||||
data.size = uint32(unsafe.Sizeof(*data))
|
||||
|
||||
return data, setupDiGetDeviceInfoListDetail(deviceInfoSet, data)
|
||||
}
|
||||
|
||||
// GetDeviceInfoListDetail method retrieves information associated with a device information set including the class GUID, remote computer handle, and remote computer name.
|
||||
func (deviceInfoSet DevInfo) GetDeviceInfoListDetail() (*DevInfoListDetailData, error) {
|
||||
return SetupDiGetDeviceInfoListDetail(deviceInfoSet)
|
||||
}
|
||||
|
||||
//sys setupDiCreateDeviceInfo(deviceInfoSet DevInfo, DeviceName *uint16, classGUID *windows.GUID, DeviceDescription *uint16, hwndParent uintptr, CreationFlags DICD, deviceInfoData *DevInfoData) (err error) = setupapi.SetupDiCreateDeviceInfoW
|
||||
|
||||
// SetupDiCreateDeviceInfo function creates a new device information element and adds it as a new member to the specified device information set.
|
||||
func SetupDiCreateDeviceInfo(deviceInfoSet DevInfo, deviceName string, classGUID *windows.GUID, deviceDescription string, hwndParent uintptr, creationFlags DICD) (deviceInfoData *DevInfoData, err error) {
|
||||
deviceNameUTF16, err := syscall.UTF16PtrFromString(deviceName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var deviceDescriptionUTF16 *uint16
|
||||
if deviceDescription != "" {
|
||||
deviceDescriptionUTF16, err = syscall.UTF16PtrFromString(deviceDescription)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
data := &DevInfoData{}
|
||||
data.size = uint32(unsafe.Sizeof(*data))
|
||||
|
||||
return data, setupDiCreateDeviceInfo(deviceInfoSet, deviceNameUTF16, classGUID, deviceDescriptionUTF16, hwndParent, creationFlags, data)
|
||||
}
|
||||
|
||||
// CreateDeviceInfo method creates a new device information element and adds it as a new member to the specified device information set.
|
||||
func (deviceInfoSet DevInfo) CreateDeviceInfo(deviceName string, classGUID *windows.GUID, deviceDescription string, hwndParent uintptr, creationFlags DICD) (*DevInfoData, error) {
|
||||
return SetupDiCreateDeviceInfo(deviceInfoSet, deviceName, classGUID, deviceDescription, hwndParent, creationFlags)
|
||||
}
|
||||
|
||||
//sys setupDiEnumDeviceInfo(deviceInfoSet DevInfo, memberIndex uint32, deviceInfoData *DevInfoData) (err error) = setupapi.SetupDiEnumDeviceInfo
|
||||
|
||||
// SetupDiEnumDeviceInfo function returns a DevInfoData structure that specifies a device information element in a device information set.
|
||||
func SetupDiEnumDeviceInfo(deviceInfoSet DevInfo, memberIndex int) (*DevInfoData, error) {
|
||||
data := &DevInfoData{}
|
||||
data.size = uint32(unsafe.Sizeof(*data))
|
||||
|
||||
return data, setupDiEnumDeviceInfo(deviceInfoSet, uint32(memberIndex), data)
|
||||
}
|
||||
|
||||
// EnumDeviceInfo method returns a DevInfoData structure that specifies a device information element in a device information set.
|
||||
func (deviceInfoSet DevInfo) EnumDeviceInfo(memberIndex int) (*DevInfoData, error) {
|
||||
return SetupDiEnumDeviceInfo(deviceInfoSet, memberIndex)
|
||||
}
|
||||
|
||||
// SetupDiDestroyDeviceInfoList function deletes a device information set and frees all associated memory.
|
||||
//sys SetupDiDestroyDeviceInfoList(deviceInfoSet DevInfo) (err error) = setupapi.SetupDiDestroyDeviceInfoList
|
||||
|
||||
// Close method deletes a device information set and frees all associated memory.
|
||||
func (deviceInfoSet DevInfo) Close() error {
|
||||
return SetupDiDestroyDeviceInfoList(deviceInfoSet)
|
||||
}
|
||||
|
||||
//sys SetupDiBuildDriverInfoList(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT) (err error) = setupapi.SetupDiBuildDriverInfoList
|
||||
|
||||
// BuildDriverInfoList method builds a list of drivers that is associated with a specific device or with the global class driver list for a device information set.
|
||||
func (deviceInfoSet DevInfo) BuildDriverInfoList(deviceInfoData *DevInfoData, driverType SPDIT) error {
|
||||
return SetupDiBuildDriverInfoList(deviceInfoSet, deviceInfoData, driverType)
|
||||
}
|
||||
|
||||
//sys SetupDiCancelDriverInfoSearch(deviceInfoSet DevInfo) (err error) = setupapi.SetupDiCancelDriverInfoSearch
|
||||
|
||||
// CancelDriverInfoSearch method cancels a driver list search that is currently in progress in a different thread.
|
||||
func (deviceInfoSet DevInfo) CancelDriverInfoSearch() error {
|
||||
return SetupDiCancelDriverInfoSearch(deviceInfoSet)
|
||||
}
|
||||
|
||||
//sys setupDiEnumDriverInfo(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT, memberIndex uint32, driverInfoData *DrvInfoData) (err error) = setupapi.SetupDiEnumDriverInfoW
|
||||
|
||||
// SetupDiEnumDriverInfo function enumerates the members of a driver list.
|
||||
func SetupDiEnumDriverInfo(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT, memberIndex int) (*DrvInfoData, error) {
|
||||
data := &DrvInfoData{}
|
||||
data.size = uint32(unsafe.Sizeof(*data))
|
||||
|
||||
return data, setupDiEnumDriverInfo(deviceInfoSet, deviceInfoData, driverType, uint32(memberIndex), data)
|
||||
}
|
||||
|
||||
// EnumDriverInfo method enumerates the members of a driver list.
|
||||
func (deviceInfoSet DevInfo) EnumDriverInfo(deviceInfoData *DevInfoData, driverType SPDIT, memberIndex int) (*DrvInfoData, error) {
|
||||
return SetupDiEnumDriverInfo(deviceInfoSet, deviceInfoData, driverType, memberIndex)
|
||||
}
|
||||
|
||||
//sys setupDiGetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (err error) = setupapi.SetupDiGetSelectedDriverW
|
||||
|
||||
// SetupDiGetSelectedDriver function retrieves the selected driver for a device information set or a particular device information element.
|
||||
func SetupDiGetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (*DrvInfoData, error) {
|
||||
data := &DrvInfoData{}
|
||||
data.size = uint32(unsafe.Sizeof(*data))
|
||||
|
||||
return data, setupDiGetSelectedDriver(deviceInfoSet, deviceInfoData, data)
|
||||
}
|
||||
|
||||
// GetSelectedDriver method retrieves the selected driver for a device information set or a particular device information element.
|
||||
func (deviceInfoSet DevInfo) GetSelectedDriver(deviceInfoData *DevInfoData) (*DrvInfoData, error) {
|
||||
return SetupDiGetSelectedDriver(deviceInfoSet, deviceInfoData)
|
||||
}
|
||||
|
||||
//sys SetupDiSetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (err error) = setupapi.SetupDiSetSelectedDriverW
|
||||
|
||||
// SetSelectedDriver method sets, or resets, the selected driver for a device information element or the selected class driver for a device information set.
|
||||
func (deviceInfoSet DevInfo) SetSelectedDriver(deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) error {
|
||||
return SetupDiSetSelectedDriver(deviceInfoSet, deviceInfoData, driverInfoData)
|
||||
}
|
||||
|
||||
//sys setupDiGetDriverInfoDetail(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData, driverInfoDetailData *DrvInfoDetailData, driverInfoDetailDataSize uint32, requiredSize *uint32) (err error) = setupapi.SetupDiGetDriverInfoDetailW
|
||||
|
||||
// SetupDiGetDriverInfoDetail function retrieves driver information detail for a device information set or a particular device information element in the device information set.
|
||||
func SetupDiGetDriverInfoDetail(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (*DrvInfoDetailData, error) {
|
||||
const bufCapacity = 0x800
|
||||
buf := [bufCapacity]byte{}
|
||||
var bufLen uint32
|
||||
|
||||
data := (*DrvInfoDetailData)(unsafe.Pointer(&buf[0]))
|
||||
data.size = uint32(unsafe.Sizeof(*data))
|
||||
|
||||
err := setupDiGetDriverInfoDetail(deviceInfoSet, deviceInfoData, driverInfoData, data, bufCapacity, &bufLen)
|
||||
if err == nil {
|
||||
// The buffer was was sufficiently big.
|
||||
data.size = bufLen
|
||||
return data, nil
|
||||
}
|
||||
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == windows.ERROR_INSUFFICIENT_BUFFER {
|
||||
// The buffer was too small. Now that we got the required size, create another one big enough and retry.
|
||||
buf := make([]byte, bufLen)
|
||||
data := (*DrvInfoDetailData)(unsafe.Pointer(&buf[0]))
|
||||
data.size = uint32(unsafe.Sizeof(*data))
|
||||
|
||||
err = setupDiGetDriverInfoDetail(deviceInfoSet, deviceInfoData, driverInfoData, data, bufLen, &bufLen)
|
||||
if err == nil {
|
||||
data.size = bufLen
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GetDriverInfoDetail method retrieves driver information detail for a device information set or a particular device information element in the device information set.
|
||||
func (deviceInfoSet DevInfo) GetDriverInfoDetail(deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (*DrvInfoDetailData, error) {
|
||||
return SetupDiGetDriverInfoDetail(deviceInfoSet, deviceInfoData, driverInfoData)
|
||||
}
|
||||
|
||||
//sys SetupDiDestroyDriverInfoList(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT) (err error) = setupapi.SetupDiDestroyDriverInfoList
|
||||
|
||||
// DestroyDriverInfoList method deletes a driver list.
|
||||
func (deviceInfoSet DevInfo) DestroyDriverInfoList(deviceInfoData *DevInfoData, driverType SPDIT) error {
|
||||
return SetupDiDestroyDriverInfoList(deviceInfoSet, deviceInfoData, driverType)
|
||||
}
|
||||
|
||||
//sys setupDiGetClassDevsEx(classGUID *windows.GUID, Enumerator *uint16, hwndParent uintptr, Flags DIGCF, deviceInfoSet DevInfo, machineName *uint16, reserved uintptr) (handle DevInfo, err error) [failretval==DevInfo(windows.InvalidHandle)] = setupapi.SetupDiGetClassDevsExW
|
||||
|
||||
// SetupDiGetClassDevsEx function returns a handle to a device information set that contains requested device information elements for a local or a remote computer.
|
||||
func SetupDiGetClassDevsEx(classGUID *windows.GUID, enumerator string, hwndParent uintptr, flags DIGCF, deviceInfoSet DevInfo, machineName string) (handle DevInfo, err error) {
|
||||
var enumeratorUTF16 *uint16
|
||||
if enumerator != "" {
|
||||
enumeratorUTF16, err = syscall.UTF16PtrFromString(enumerator)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
var machineNameUTF16 *uint16
|
||||
if machineName != "" {
|
||||
machineNameUTF16, err = syscall.UTF16PtrFromString(machineName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return setupDiGetClassDevsEx(classGUID, enumeratorUTF16, hwndParent, flags, deviceInfoSet, machineNameUTF16, 0)
|
||||
}
|
||||
|
||||
// SetupDiCallClassInstaller function calls the appropriate class installer, and any registered co-installers, with the specified installation request (DIF code).
|
||||
//sys SetupDiCallClassInstaller(installFunction DI_FUNCTION, deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) = setupapi.SetupDiCallClassInstaller
|
||||
|
||||
// CallClassInstaller member calls the appropriate class installer, and any registered co-installers, with the specified installation request (DIF code).
|
||||
func (deviceInfoSet DevInfo) CallClassInstaller(installFunction DI_FUNCTION, deviceInfoData *DevInfoData) error {
|
||||
return SetupDiCallClassInstaller(installFunction, deviceInfoSet, deviceInfoData)
|
||||
}
|
||||
|
||||
//sys setupDiOpenDevRegKey(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, Scope DICS_FLAG, HwProfile uint32, KeyType DIREG, samDesired uint32) (key windows.Handle, err error) [failretval==windows.InvalidHandle] = setupapi.SetupDiOpenDevRegKey
|
||||
|
||||
// SetupDiOpenDevRegKey function opens a registry key for device-specific configuration information.
|
||||
func SetupDiOpenDevRegKey(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, scope DICS_FLAG, hwProfile uint32, keyType DIREG, samDesired uint32) (registry.Key, error) {
|
||||
handle, err := setupDiOpenDevRegKey(deviceInfoSet, deviceInfoData, scope, hwProfile, keyType, samDesired)
|
||||
return registry.Key(handle), err
|
||||
}
|
||||
|
||||
// OpenDevRegKey method opens a registry key for device-specific configuration information.
|
||||
func (deviceInfoSet DevInfo) OpenDevRegKey(DeviceInfoData *DevInfoData, Scope DICS_FLAG, HwProfile uint32, KeyType DIREG, samDesired uint32) (registry.Key, error) {
|
||||
return SetupDiOpenDevRegKey(deviceInfoSet, DeviceInfoData, Scope, HwProfile, KeyType, samDesired)
|
||||
}
|
||||
|
||||
//sys setupDiGetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP, propertyRegDataType *uint32, propertyBuffer *byte, propertyBufferSize uint32, requiredSize *uint32) (err error) = setupapi.SetupDiGetDeviceRegistryPropertyW
|
||||
|
||||
// SetupDiGetDeviceRegistryProperty function retrieves a specified Plug and Play device property.
|
||||
func SetupDiGetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP) (value interface{}, err error) {
|
||||
buf := make([]byte, 0x100)
|
||||
var dataType, bufLen uint32
|
||||
err = setupDiGetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property, &dataType, &buf[0], uint32(cap(buf)), &bufLen)
|
||||
if err == nil {
|
||||
// The buffer was sufficiently big.
|
||||
return getRegistryValue(buf[:bufLen], dataType)
|
||||
}
|
||||
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == windows.ERROR_INSUFFICIENT_BUFFER {
|
||||
// The buffer was too small. Now that we got the required size, create another one big enough and retry.
|
||||
buf = make([]byte, bufLen)
|
||||
err = setupDiGetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property, &dataType, &buf[0], uint32(cap(buf)), &bufLen)
|
||||
if err == nil {
|
||||
return getRegistryValue(buf[:bufLen], dataType)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getRegistryValue(buf []byte, dataType uint32) (interface{}, error) {
|
||||
switch dataType {
|
||||
case windows.REG_SZ:
|
||||
return windows.UTF16ToString(BufToUTF16(buf)), nil
|
||||
case windows.REG_EXPAND_SZ:
|
||||
return registry.ExpandString(windows.UTF16ToString(BufToUTF16(buf)))
|
||||
case windows.REG_BINARY:
|
||||
return buf, nil
|
||||
case windows.REG_DWORD_LITTLE_ENDIAN:
|
||||
return binary.LittleEndian.Uint32(buf), nil
|
||||
case windows.REG_DWORD_BIG_ENDIAN:
|
||||
return binary.BigEndian.Uint32(buf), nil
|
||||
case windows.REG_MULTI_SZ:
|
||||
bufW := BufToUTF16(buf)
|
||||
a := []string{}
|
||||
for i := 0; i < len(bufW); {
|
||||
j := i + wcslen(bufW[i:])
|
||||
if i < j {
|
||||
a = append(a, windows.UTF16ToString(bufW[i:j]))
|
||||
}
|
||||
i = j + 1
|
||||
}
|
||||
return a, nil
|
||||
case windows.REG_QWORD_LITTLE_ENDIAN:
|
||||
return binary.LittleEndian.Uint64(buf), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("Unsupported registry value type: %v", dataType)
|
||||
}
|
||||
}
|
||||
|
||||
// BufToUTF16 function reinterprets []byte buffer as []uint16
|
||||
func BufToUTF16(buf []byte) []uint16 {
|
||||
sl := struct {
|
||||
addr *uint16
|
||||
len int
|
||||
cap int
|
||||
}{(*uint16)(unsafe.Pointer(&buf[0])), len(buf) / 2, cap(buf) / 2}
|
||||
return *(*[]uint16)(unsafe.Pointer(&sl))
|
||||
}
|
||||
|
||||
// UTF16ToBuf function reinterprets []uint16 as []byte
|
||||
func UTF16ToBuf(buf []uint16) []byte {
|
||||
sl := struct {
|
||||
addr *byte
|
||||
len int
|
||||
cap int
|
||||
}{(*byte)(unsafe.Pointer(&buf[0])), len(buf) * 2, cap(buf) * 2}
|
||||
return *(*[]byte)(unsafe.Pointer(&sl))
|
||||
}
|
||||
|
||||
func wcslen(str []uint16) int {
|
||||
for i := 0; i < len(str); i++ {
|
||||
if str[i] == 0 {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return len(str)
|
||||
}
|
||||
|
||||
// GetDeviceRegistryProperty method retrieves a specified Plug and Play device property.
|
||||
func (deviceInfoSet DevInfo) GetDeviceRegistryProperty(deviceInfoData *DevInfoData, property SPDRP) (interface{}, error) {
|
||||
return SetupDiGetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property)
|
||||
}
|
||||
|
||||
//sys setupDiSetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP, propertyBuffer *byte, propertyBufferSize uint32) (err error) = setupapi.SetupDiSetDeviceRegistryPropertyW
|
||||
|
||||
// SetupDiSetDeviceRegistryProperty function sets a Plug and Play device property for a device.
|
||||
func SetupDiSetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP, propertyBuffers []byte) error {
|
||||
return setupDiSetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property, &propertyBuffers[0], uint32(len(propertyBuffers)))
|
||||
}
|
||||
|
||||
// SetDeviceRegistryProperty function sets a Plug and Play device property for a device.
|
||||
func (deviceInfoSet DevInfo) SetDeviceRegistryProperty(deviceInfoData *DevInfoData, property SPDRP, propertyBuffers []byte) error {
|
||||
return SetupDiSetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property, propertyBuffers)
|
||||
}
|
||||
|
||||
//sys setupDiGetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, deviceInstallParams *DevInstallParams) (err error) = setupapi.SetupDiGetDeviceInstallParamsW
|
||||
|
||||
// SetupDiGetDeviceInstallParams function retrieves device installation parameters for a device information set or a particular device information element.
|
||||
func SetupDiGetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (*DevInstallParams, error) {
|
||||
params := &DevInstallParams{}
|
||||
params.size = uint32(unsafe.Sizeof(*params))
|
||||
|
||||
return params, setupDiGetDeviceInstallParams(deviceInfoSet, deviceInfoData, params)
|
||||
}
|
||||
|
||||
// GetDeviceInstallParams method retrieves device installation parameters for a device information set or a particular device information element.
|
||||
func (deviceInfoSet DevInfo) GetDeviceInstallParams(deviceInfoData *DevInfoData) (*DevInstallParams, error) {
|
||||
return SetupDiGetDeviceInstallParams(deviceInfoSet, deviceInfoData)
|
||||
}
|
||||
|
||||
// SetupDiGetClassInstallParams function retrieves class installation parameters for a device information set or a particular device information element.
|
||||
//sys SetupDiGetClassInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32, requiredSize *uint32) (err error) = setupapi.SetupDiGetClassInstallParamsW
|
||||
|
||||
// GetClassInstallParams method retrieves class installation parameters for a device information set or a particular device information element.
|
||||
func (deviceInfoSet DevInfo) GetClassInstallParams(deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32, requiredSize *uint32) error {
|
||||
return SetupDiGetClassInstallParams(deviceInfoSet, deviceInfoData, classInstallParams, classInstallParamsSize, requiredSize)
|
||||
}
|
||||
|
||||
//sys SetupDiSetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, deviceInstallParams *DevInstallParams) (err error) = setupapi.SetupDiSetDeviceInstallParamsW
|
||||
|
||||
// SetDeviceInstallParams member sets device installation parameters for a device information set or a particular device information element.
|
||||
func (deviceInfoSet DevInfo) SetDeviceInstallParams(deviceInfoData *DevInfoData, deviceInstallParams *DevInstallParams) error {
|
||||
return SetupDiSetDeviceInstallParams(deviceInfoSet, deviceInfoData, deviceInstallParams)
|
||||
}
|
||||
|
||||
// SetupDiSetClassInstallParams function sets or clears class install parameters for a device information set or a particular device information element.
|
||||
//sys SetupDiSetClassInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32) (err error) = setupapi.SetupDiSetClassInstallParamsW
|
||||
|
||||
// SetClassInstallParams method sets or clears class install parameters for a device information set or a particular device information element.
|
||||
func (deviceInfoSet DevInfo) SetClassInstallParams(deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32) error {
|
||||
return SetupDiSetClassInstallParams(deviceInfoSet, deviceInfoData, classInstallParams, classInstallParamsSize)
|
||||
}
|
||||
|
||||
//sys setupDiClassNameFromGuidEx(classGUID *windows.GUID, className *uint16, classNameSize uint32, requiredSize *uint32, machineName *uint16, reserved uintptr) (err error) = setupapi.SetupDiClassNameFromGuidExW
|
||||
|
||||
// SetupDiClassNameFromGuidEx function retrieves the class name associated with a class GUID. The class can be installed on a local or remote computer.
|
||||
func SetupDiClassNameFromGuidEx(classGUID *windows.GUID, machineName string) (className string, err error) {
|
||||
var classNameUTF16 [MAX_CLASS_NAME_LEN]uint16
|
||||
|
||||
var machineNameUTF16 *uint16
|
||||
if machineName != "" {
|
||||
machineNameUTF16, err = syscall.UTF16PtrFromString(machineName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = setupDiClassNameFromGuidEx(classGUID, &classNameUTF16[0], MAX_CLASS_NAME_LEN, nil, machineNameUTF16, 0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
className = windows.UTF16ToString(classNameUTF16[:])
|
||||
return
|
||||
}
|
||||
|
||||
//sys setupDiClassGuidsFromNameEx(className *uint16, classGuidList *windows.GUID, classGuidListSize uint32, requiredSize *uint32, machineName *uint16, reserved uintptr) (err error) = setupapi.SetupDiClassGuidsFromNameExW
|
||||
|
||||
// SetupDiClassGuidsFromNameEx function retrieves the GUIDs associated with the specified class name. This resulting list contains the classes currently installed on a local or remote computer.
|
||||
func SetupDiClassGuidsFromNameEx(className string, machineName string) ([]windows.GUID, error) {
|
||||
classNameUTF16, err := syscall.UTF16PtrFromString(className)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
const bufCapacity = 4
|
||||
var buf [bufCapacity]windows.GUID
|
||||
var bufLen uint32
|
||||
|
||||
var machineNameUTF16 *uint16
|
||||
if machineName != "" {
|
||||
machineNameUTF16, err = syscall.UTF16PtrFromString(machineName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
err = setupDiClassGuidsFromNameEx(classNameUTF16, &buf[0], bufCapacity, &bufLen, machineNameUTF16, 0)
|
||||
if err == nil {
|
||||
// The GUID array was sufficiently big. Return its slice.
|
||||
return buf[:bufLen], nil
|
||||
}
|
||||
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == windows.ERROR_INSUFFICIENT_BUFFER {
|
||||
// The GUID array was too small. Now that we got the required size, create another one big enough and retry.
|
||||
buf := make([]windows.GUID, bufLen)
|
||||
err = setupDiClassGuidsFromNameEx(classNameUTF16, &buf[0], bufLen, &bufLen, machineNameUTF16, 0)
|
||||
if err == nil {
|
||||
return buf[:bufLen], nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//sys setupDiGetSelectedDevice(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) = setupapi.SetupDiGetSelectedDevice
|
||||
|
||||
// SetupDiGetSelectedDevice function retrieves the selected device information element in a device information set.
|
||||
func SetupDiGetSelectedDevice(deviceInfoSet DevInfo) (*DevInfoData, error) {
|
||||
data := &DevInfoData{}
|
||||
data.size = uint32(unsafe.Sizeof(*data))
|
||||
|
||||
return data, setupDiGetSelectedDevice(deviceInfoSet, data)
|
||||
}
|
||||
|
||||
// GetSelectedDevice method retrieves the selected device information element in a device information set.
|
||||
func (deviceInfoSet DevInfo) GetSelectedDevice() (*DevInfoData, error) {
|
||||
return SetupDiGetSelectedDevice(deviceInfoSet)
|
||||
}
|
||||
|
||||
// SetupDiSetSelectedDevice function sets a device information element as the selected member of a device information set. This function is typically used by an installation wizard.
|
||||
//sys SetupDiSetSelectedDevice(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) = setupapi.SetupDiSetSelectedDevice
|
||||
|
||||
// SetSelectedDevice method sets a device information element as the selected member of a device information set. This function is typically used by an installation wizard.
|
||||
func (deviceInfoSet DevInfo) SetSelectedDevice(deviceInfoData *DevInfoData) error {
|
||||
return SetupDiSetSelectedDevice(deviceInfoSet, deviceInfoData)
|
||||
}
|
||||
483
tun/wintun/setupapi/setupapi_windows_test.go
Normal file
483
tun/wintun/setupapi/setupapi_windows_test.go
Normal file
@@ -0,0 +1,483 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package setupapi
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.zx2c4.com/wireguard/tun/wintun/guid"
|
||||
)
|
||||
|
||||
var deviceClassNetGUID = windows.GUID{Data1: 0x4d36e972, Data2: 0xe325, Data3: 0x11ce, Data4: [8]byte{0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18}}
|
||||
var computerName string
|
||||
|
||||
func init() {
|
||||
computerName, _ = windows.ComputerName()
|
||||
}
|
||||
|
||||
func TestSetupDiCreateDeviceInfoListEx(t *testing.T) {
|
||||
devInfoList, err := SetupDiCreateDeviceInfoListEx(&deviceClassNetGUID, 0, "")
|
||||
if err == nil {
|
||||
devInfoList.Close()
|
||||
} else {
|
||||
t.Errorf("Error calling SetupDiCreateDeviceInfoListEx: %s", err.Error())
|
||||
}
|
||||
|
||||
devInfoList, err = SetupDiCreateDeviceInfoListEx(&deviceClassNetGUID, 0, computerName)
|
||||
if err == nil {
|
||||
devInfoList.Close()
|
||||
} else {
|
||||
t.Errorf("Error calling SetupDiCreateDeviceInfoListEx: %s", err.Error())
|
||||
}
|
||||
|
||||
devInfoList, err = SetupDiCreateDeviceInfoListEx(nil, 0, "")
|
||||
if err == nil {
|
||||
devInfoList.Close()
|
||||
} else {
|
||||
t.Errorf("Error calling SetupDiCreateDeviceInfoListEx(nil): %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupDiGetDeviceInfoListDetail(t *testing.T) {
|
||||
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
|
||||
}
|
||||
defer devInfoList.Close()
|
||||
|
||||
data, err := devInfoList.GetDeviceInfoListDetail()
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetDeviceInfoListDetail: %s", err.Error())
|
||||
} else {
|
||||
if data.ClassGUID != deviceClassNetGUID {
|
||||
t.Error("SetupDiGetDeviceInfoListDetail returned different class GUID")
|
||||
}
|
||||
|
||||
if data.RemoteMachineHandle != windows.Handle(0) {
|
||||
t.Error("SetupDiGetDeviceInfoListDetail returned non-NULL remote machine handle")
|
||||
}
|
||||
|
||||
if data.GetRemoteMachineName() != "" {
|
||||
t.Error("SetupDiGetDeviceInfoListDetail returned non-NULL remote machine name")
|
||||
}
|
||||
}
|
||||
|
||||
devInfoList, err = SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), computerName)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
|
||||
}
|
||||
defer devInfoList.Close()
|
||||
|
||||
data, err = devInfoList.GetDeviceInfoListDetail()
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetDeviceInfoListDetail: %s", err.Error())
|
||||
} else {
|
||||
if data.ClassGUID != deviceClassNetGUID {
|
||||
t.Error("SetupDiGetDeviceInfoListDetail returned different class GUID")
|
||||
}
|
||||
|
||||
if data.RemoteMachineHandle == windows.Handle(0) {
|
||||
t.Error("SetupDiGetDeviceInfoListDetail returned NULL remote machine handle")
|
||||
}
|
||||
|
||||
if data.GetRemoteMachineName() != computerName {
|
||||
t.Error("SetupDiGetDeviceInfoListDetail returned different remote machine name")
|
||||
}
|
||||
}
|
||||
|
||||
data = &DevInfoListDetailData{}
|
||||
data.SetRemoteMachineName("foobar")
|
||||
if data.GetRemoteMachineName() != "foobar" {
|
||||
t.Error("DevInfoListDetailData.(Get|Set)RemoteMachineName() differ")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupDiCreateDeviceInfo(t *testing.T) {
|
||||
devInfoList, err := SetupDiCreateDeviceInfoListEx(&deviceClassNetGUID, 0, computerName)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiCreateDeviceInfoListEx: %s", err.Error())
|
||||
}
|
||||
defer devInfoList.Close()
|
||||
|
||||
deviceClassNetName, err := SetupDiClassNameFromGuidEx(&deviceClassNetGUID, computerName)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiClassNameFromGuidEx: %s", err.Error())
|
||||
}
|
||||
|
||||
devInfoData, err := devInfoList.CreateDeviceInfo(deviceClassNetName, &deviceClassNetGUID, "This is a test device", 0, DICD_GENERATE_ID)
|
||||
if err != nil {
|
||||
// Access denied is expected, as the SetupDiCreateDeviceInfo() require elevation to succeed.
|
||||
if errWin, ok := err.(syscall.Errno); !ok || errWin != windows.ERROR_ACCESS_DENIED {
|
||||
t.Errorf("Error calling SetupDiCreateDeviceInfo: %s", err.Error())
|
||||
}
|
||||
} else if devInfoData.ClassGUID != deviceClassNetGUID {
|
||||
t.Error("SetupDiCreateDeviceInfo returned different class GUID")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupDiEnumDeviceInfo(t *testing.T) {
|
||||
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
|
||||
}
|
||||
defer devInfoList.Close()
|
||||
|
||||
for i := 0; true; i++ {
|
||||
data, err := devInfoList.EnumDeviceInfo(i)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if data.ClassGUID != deviceClassNetGUID {
|
||||
t.Error("SetupDiEnumDeviceInfo returned different class GUID")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDevInfo_BuildDriverInfoList(t *testing.T) {
|
||||
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
|
||||
}
|
||||
defer devInfoList.Close()
|
||||
|
||||
for i := 0; true; i++ {
|
||||
deviceData, err := devInfoList.EnumDeviceInfo(i)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
const driverType SPDIT = SPDIT_COMPATDRIVER
|
||||
err = devInfoList.BuildDriverInfoList(deviceData, driverType)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiBuildDriverInfoList: %s", err.Error())
|
||||
}
|
||||
defer devInfoList.DestroyDriverInfoList(deviceData, driverType)
|
||||
|
||||
var selectedDriverData *DrvInfoData
|
||||
for j := 0; true; j++ {
|
||||
driverData, err := devInfoList.EnumDriverInfo(deviceData, driverType, j)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if driverData.DriverType == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if !driverData.IsNewer(windows.Filetime{}, 0) {
|
||||
t.Error("Driver should have non-zero date and version")
|
||||
}
|
||||
if !driverData.IsNewer(windows.Filetime{HighDateTime: driverData.DriverDate.HighDateTime}, 0) {
|
||||
t.Error("Driver should have non-zero date and version")
|
||||
}
|
||||
if driverData.IsNewer(windows.Filetime{HighDateTime: driverData.DriverDate.HighDateTime + 1}, 0) {
|
||||
t.Error("Driver should report newer version on high-date-time")
|
||||
}
|
||||
if !driverData.IsNewer(windows.Filetime{HighDateTime: driverData.DriverDate.HighDateTime, LowDateTime: driverData.DriverDate.LowDateTime}, 0) {
|
||||
t.Error("Driver should have non-zero version")
|
||||
}
|
||||
if driverData.IsNewer(windows.Filetime{HighDateTime: driverData.DriverDate.HighDateTime, LowDateTime: driverData.DriverDate.LowDateTime + 1}, 0) {
|
||||
t.Error("Driver should report newer version on low-date-time")
|
||||
}
|
||||
if driverData.IsNewer(windows.Filetime{HighDateTime: driverData.DriverDate.HighDateTime, LowDateTime: driverData.DriverDate.LowDateTime}, driverData.DriverVersion) {
|
||||
t.Error("Driver should not be newer than itself")
|
||||
}
|
||||
if driverData.IsNewer(windows.Filetime{HighDateTime: driverData.DriverDate.HighDateTime, LowDateTime: driverData.DriverDate.LowDateTime}, driverData.DriverVersion+1) {
|
||||
t.Error("Driver should report newer version on version")
|
||||
}
|
||||
|
||||
err = devInfoList.SetSelectedDriver(deviceData, driverData)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiSetSelectedDriver: %s", err.Error())
|
||||
} else {
|
||||
selectedDriverData = driverData
|
||||
}
|
||||
|
||||
driverDetailData, err := devInfoList.GetDriverInfoDetail(deviceData, driverData)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetDriverInfoDetail: %s", err.Error())
|
||||
}
|
||||
|
||||
if driverDetailData.IsCompatible("foobar-aab6e3a4-144e-4786-88d3-6cec361e1edd") {
|
||||
t.Error("Invalid HWID compatibitlity reported")
|
||||
}
|
||||
if !driverDetailData.IsCompatible(strings.ToUpper(driverDetailData.GetHardwareID())) {
|
||||
t.Error("HWID compatibitlity missed")
|
||||
}
|
||||
a := driverDetailData.GetCompatIDs()
|
||||
for k := range a {
|
||||
if !driverDetailData.IsCompatible(strings.ToUpper(a[k])) {
|
||||
t.Error("HWID compatibitlity missed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectedDriverData2, err := devInfoList.GetSelectedDriver(deviceData)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetSelectedDriver: %s", err.Error())
|
||||
} else if *selectedDriverData != *selectedDriverData2 {
|
||||
t.Error("SetupDiGetSelectedDriver should return driver selected with SetupDiSetSelectedDriver")
|
||||
}
|
||||
}
|
||||
|
||||
data := &DrvInfoData{}
|
||||
data.SetDescription("foobar")
|
||||
if data.GetDescription() != "foobar" {
|
||||
t.Error("DrvInfoData.(Get|Set)Description() differ")
|
||||
}
|
||||
data.SetMfgName("foobar")
|
||||
if data.GetMfgName() != "foobar" {
|
||||
t.Error("DrvInfoData.(Get|Set)MfgName() differ")
|
||||
}
|
||||
data.SetProviderName("foobar")
|
||||
if data.GetProviderName() != "foobar" {
|
||||
t.Error("DrvInfoData.(Get|Set)ProviderName() differ")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupDiGetClassDevsEx(t *testing.T) {
|
||||
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "PCI", 0, DIGCF_PRESENT, DevInfo(0), computerName)
|
||||
if err == nil {
|
||||
devInfoList.Close()
|
||||
} else {
|
||||
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
|
||||
}
|
||||
|
||||
devInfoList, err = SetupDiGetClassDevsEx(nil, "", 0, DIGCF_PRESENT, DevInfo(0), "")
|
||||
if err == nil {
|
||||
devInfoList.Close()
|
||||
t.Errorf("SetupDiGetClassDevsEx(nil, ...) should fail")
|
||||
} else {
|
||||
if errWin, ok := err.(syscall.Errno); !ok || errWin != 87 /*ERROR_INVALID_PARAMETER*/ {
|
||||
t.Errorf("SetupDiGetClassDevsEx(nil, ...) should fail with ERROR_INVALID_PARAMETER")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupDiOpenDevRegKey(t *testing.T) {
|
||||
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
|
||||
}
|
||||
defer devInfoList.Close()
|
||||
|
||||
for i := 0; true; i++ {
|
||||
data, err := devInfoList.EnumDeviceInfo(i)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
key, err := devInfoList.OpenDevRegKey(data, DICS_FLAG_GLOBAL, 0, DIREG_DRV, windows.KEY_READ)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiOpenDevRegKey: %s", err.Error())
|
||||
}
|
||||
defer key.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupDiGetDeviceRegistryProperty(t *testing.T) {
|
||||
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
|
||||
}
|
||||
defer devInfoList.Close()
|
||||
|
||||
for i := 0; true; i++ {
|
||||
data, err := devInfoList.EnumDeviceInfo(i)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
val, err := devInfoList.GetDeviceRegistryProperty(data, SPDRP_CLASS)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetDeviceRegistryProperty(SPDRP_CLASS): %s", err.Error())
|
||||
} else if class, ok := val.(string); !ok || strings.ToLower(class) != "net" {
|
||||
t.Errorf("SetupDiGetDeviceRegistryProperty(SPDRP_CLASS) should return \"Net\"")
|
||||
}
|
||||
|
||||
val, err = devInfoList.GetDeviceRegistryProperty(data, SPDRP_CLASSGUID)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetDeviceRegistryProperty(SPDRP_CLASSGUID): %s", err.Error())
|
||||
} else if valStr, ok := val.(string); !ok {
|
||||
t.Errorf("SetupDiGetDeviceRegistryProperty(SPDRP_CLASSGUID) should return string")
|
||||
} else {
|
||||
classGUID, err := guid.FromString(valStr)
|
||||
if err != nil {
|
||||
t.Errorf("Error parsing GUID returned by SetupDiGetDeviceRegistryProperty(SPDRP_CLASSGUID): %s", err.Error())
|
||||
} else if *classGUID != deviceClassNetGUID {
|
||||
t.Errorf("SetupDiGetDeviceRegistryProperty(SPDRP_CLASSGUID) should return %x", deviceClassNetGUID)
|
||||
}
|
||||
}
|
||||
|
||||
val, err = devInfoList.GetDeviceRegistryProperty(data, SPDRP_COMPATIBLEIDS)
|
||||
if err != nil {
|
||||
// Some devices have no SPDRP_COMPATIBLEIDS.
|
||||
if errWin, ok := err.(syscall.Errno); !ok || errWin != 13 /*windows.ERROR_INVALID_DATA*/ {
|
||||
t.Errorf("Error calling SetupDiGetDeviceRegistryProperty(SPDRP_COMPATIBLEIDS): %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
val, err = devInfoList.GetDeviceRegistryProperty(data, SPDRP_CONFIGFLAGS)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetDeviceRegistryProperty(SPDRP_CONFIGFLAGS): %s", err.Error())
|
||||
}
|
||||
|
||||
val, err = devInfoList.GetDeviceRegistryProperty(data, SPDRP_DEVICE_POWER_DATA)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetDeviceRegistryProperty(SPDRP_DEVICE_POWER_DATA): %s", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupDiGetDeviceInstallParams(t *testing.T) {
|
||||
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
|
||||
}
|
||||
defer devInfoList.Close()
|
||||
|
||||
for i := 0; true; i++ {
|
||||
data, err := devInfoList.EnumDeviceInfo(i)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = devInfoList.GetDeviceInstallParams(data)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetDeviceInstallParams: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
params := &DevInstallParams{}
|
||||
params.SetDriverPath("foobar")
|
||||
if params.GetDriverPath() != "foobar" {
|
||||
t.Error("DevInstallParams.(Get|Set)DriverPath() differ")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupDiClassNameFromGuidEx(t *testing.T) {
|
||||
deviceClassNetName, err := SetupDiClassNameFromGuidEx(&deviceClassNetGUID, "")
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiClassNameFromGuidEx: %s", err.Error())
|
||||
} else if strings.ToLower(deviceClassNetName) != "net" {
|
||||
t.Errorf("SetupDiClassNameFromGuidEx(%x) should return \"Net\"", deviceClassNetGUID)
|
||||
}
|
||||
|
||||
deviceClassNetName, err = SetupDiClassNameFromGuidEx(&deviceClassNetGUID, computerName)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiClassNameFromGuidEx: %s", err.Error())
|
||||
} else if strings.ToLower(deviceClassNetName) != "net" {
|
||||
t.Errorf("SetupDiClassNameFromGuidEx(%x) should return \"Net\"", deviceClassNetGUID)
|
||||
}
|
||||
|
||||
_, err = SetupDiClassNameFromGuidEx(nil, "")
|
||||
if err == nil {
|
||||
t.Errorf("SetupDiClassNameFromGuidEx(nil) should fail")
|
||||
} else {
|
||||
if errWin, ok := err.(syscall.Errno); !ok || errWin != 1784 /*ERROR_INVALID_USER_BUFFER*/ {
|
||||
t.Errorf("SetupDiClassNameFromGuidEx(nil) should fail with ERROR_INVALID_USER_BUFFER")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupDiClassGuidsFromNameEx(t *testing.T) {
|
||||
ClassGUIDs, err := SetupDiClassGuidsFromNameEx("Net", "")
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiClassGuidsFromNameEx: %s", err.Error())
|
||||
} else {
|
||||
found := false
|
||||
for i := range ClassGUIDs {
|
||||
if ClassGUIDs[i] == deviceClassNetGUID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("SetupDiClassGuidsFromNameEx(\"Net\") should return %x", deviceClassNetGUID)
|
||||
}
|
||||
}
|
||||
|
||||
ClassGUIDs, err = SetupDiClassGuidsFromNameEx("foobar-34274a51-a6e6-45f0-80d6-c62be96dd5fe", computerName)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiClassGuidsFromNameEx: %s", err.Error())
|
||||
} else if len(ClassGUIDs) != 0 {
|
||||
t.Errorf("SetupDiClassGuidsFromNameEx(\"foobar-34274a51-a6e6-45f0-80d6-c62be96dd5fe\") should return an empty GUID set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupDiGetSelectedDevice(t *testing.T) {
|
||||
devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "")
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error())
|
||||
}
|
||||
defer devInfoList.Close()
|
||||
|
||||
for i := 0; true; i++ {
|
||||
data, err := devInfoList.EnumDeviceInfo(i)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
err = devInfoList.SetSelectedDevice(data)
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiSetSelectedDevice: %s", err.Error())
|
||||
}
|
||||
|
||||
data2, err := devInfoList.GetSelectedDevice()
|
||||
if err != nil {
|
||||
t.Errorf("Error calling SetupDiGetSelectedDevice: %s", err.Error())
|
||||
} else if *data != *data2 {
|
||||
t.Error("SetupDiGetSelectedDevice returned different data than was set by SetupDiSetSelectedDevice")
|
||||
}
|
||||
}
|
||||
|
||||
err = devInfoList.SetSelectedDevice(nil)
|
||||
if err == nil {
|
||||
t.Errorf("SetupDiSetSelectedDevice(nil) should fail")
|
||||
} else {
|
||||
if errWin, ok := err.(syscall.Errno); !ok || errWin != 87 /*ERROR_INVALID_PARAMETER*/ {
|
||||
t.Errorf("SetupDiSetSelectedDevice(nil) should fail with ERROR_INVALID_USER_BUFFER")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUTF16ToBuf(t *testing.T) {
|
||||
buf := []uint16{0x0123, 0x4567, 0x89ab, 0xcdef}
|
||||
buf2 := UTF16ToBuf(buf)
|
||||
if len(buf)*2 != len(buf2) ||
|
||||
cap(buf)*2 != cap(buf2) ||
|
||||
buf2[0] != 0x23 || buf2[1] != 0x01 ||
|
||||
buf2[2] != 0x67 || buf2[3] != 0x45 ||
|
||||
buf2[4] != 0xab || buf2[5] != 0x89 ||
|
||||
buf2[6] != 0xef || buf2[7] != 0xcd {
|
||||
t.Errorf("SetupDiSetSelectedDevice(nil) should fail with ERROR_INVALID_USER_BUFFER")
|
||||
}
|
||||
}
|
||||
539
tun/wintun/setupapi/types_windows.go
Normal file
539
tun/wintun/setupapi/types_windows.go
Normal file
@@ -0,0 +1,539 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package setupapi
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
const (
|
||||
MAX_DEVICE_ID_LEN = 200
|
||||
MAX_DEVNODE_ID_LEN = MAX_DEVICE_ID_LEN
|
||||
MAX_GUID_STRING_LEN = 39 // 38 chars + terminator null
|
||||
MAX_CLASS_NAME_LEN = 32
|
||||
MAX_PROFILE_LEN = 80
|
||||
MAX_CONFIG_VALUE = 9999
|
||||
MAX_INSTANCE_VALUE = 9999
|
||||
CONFIGMG_VERSION = 0x0400
|
||||
)
|
||||
|
||||
//
|
||||
// Define maximum string length constants
|
||||
//
|
||||
const (
|
||||
LINE_LEN = 256 // Windows 9x-compatible maximum for displayable strings coming from a device INF.
|
||||
MAX_INF_STRING_LENGTH = 4096 // Actual maximum size of an INF string (including string substitutions).
|
||||
MAX_INF_SECTION_NAME_LENGTH = 255 // For Windows 9x compatibility, INF section names should be constrained to 32 characters.
|
||||
MAX_TITLE_LEN = 60
|
||||
MAX_INSTRUCTION_LEN = 256
|
||||
MAX_LABEL_LEN = 30
|
||||
MAX_SERVICE_NAME_LEN = 256
|
||||
MAX_SUBTITLE_LEN = 256
|
||||
)
|
||||
|
||||
const (
|
||||
// SP_MAX_MACHINENAME_LENGTH defines maximum length of a machine name in the format expected by ConfigMgr32 CM_Connect_Machine (i.e., "\\\\MachineName\0").
|
||||
SP_MAX_MACHINENAME_LENGTH = windows.MAX_PATH + 3
|
||||
)
|
||||
|
||||
// HSPFILEQ is type for setup file queue
|
||||
type HSPFILEQ uintptr
|
||||
|
||||
// DevInfo holds reference to device information set
|
||||
type DevInfo windows.Handle
|
||||
|
||||
// DevInfoData is a device information structure (references a device instance that is a member of a device information set)
|
||||
type DevInfoData struct {
|
||||
size uint32
|
||||
ClassGUID windows.GUID
|
||||
DevInst uint32 // DEVINST handle
|
||||
_ uintptr
|
||||
}
|
||||
|
||||
// DevInfoListDetailData is a structure for detailed information on a device information set (used for SetupDiGetDeviceInfoListDetail which supercedes the functionality of SetupDiGetDeviceInfoListClass).
|
||||
type DevInfoListDetailData struct {
|
||||
size uint32
|
||||
ClassGUID windows.GUID
|
||||
RemoteMachineHandle windows.Handle
|
||||
remoteMachineName [SP_MAX_MACHINENAME_LENGTH]uint16
|
||||
}
|
||||
|
||||
func (data *DevInfoListDetailData) GetRemoteMachineName() string {
|
||||
return windows.UTF16ToString(data.remoteMachineName[:])
|
||||
}
|
||||
|
||||
func (data *DevInfoListDetailData) SetRemoteMachineName(remoteMachineName string) error {
|
||||
str, err := syscall.UTF16FromString(remoteMachineName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(data.remoteMachineName[:], str)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DI_FUNCTION is function type for device installer
|
||||
type DI_FUNCTION uint32
|
||||
|
||||
const (
|
||||
DIF_SELECTDEVICE DI_FUNCTION = 0x00000001
|
||||
DIF_INSTALLDEVICE DI_FUNCTION = 0x00000002
|
||||
DIF_ASSIGNRESOURCES DI_FUNCTION = 0x00000003
|
||||
DIF_PROPERTIES DI_FUNCTION = 0x00000004
|
||||
DIF_REMOVE DI_FUNCTION = 0x00000005
|
||||
DIF_FIRSTTIMESETUP DI_FUNCTION = 0x00000006
|
||||
DIF_FOUNDDEVICE DI_FUNCTION = 0x00000007
|
||||
DIF_SELECTCLASSDRIVERS DI_FUNCTION = 0x00000008
|
||||
DIF_VALIDATECLASSDRIVERS DI_FUNCTION = 0x00000009
|
||||
DIF_INSTALLCLASSDRIVERS DI_FUNCTION = 0x0000000A
|
||||
DIF_CALCDISKSPACE DI_FUNCTION = 0x0000000B
|
||||
DIF_DESTROYPRIVATEDATA DI_FUNCTION = 0x0000000C
|
||||
DIF_VALIDATEDRIVER DI_FUNCTION = 0x0000000D
|
||||
DIF_DETECT DI_FUNCTION = 0x0000000F
|
||||
DIF_INSTALLWIZARD DI_FUNCTION = 0x00000010
|
||||
DIF_DESTROYWIZARDDATA DI_FUNCTION = 0x00000011
|
||||
DIF_PROPERTYCHANGE DI_FUNCTION = 0x00000012
|
||||
DIF_ENABLECLASS DI_FUNCTION = 0x00000013
|
||||
DIF_DETECTVERIFY DI_FUNCTION = 0x00000014
|
||||
DIF_INSTALLDEVICEFILES DI_FUNCTION = 0x00000015
|
||||
DIF_UNREMOVE DI_FUNCTION = 0x00000016
|
||||
DIF_SELECTBESTCOMPATDRV DI_FUNCTION = 0x00000017
|
||||
DIF_ALLOW_INSTALL DI_FUNCTION = 0x00000018
|
||||
DIF_REGISTERDEVICE DI_FUNCTION = 0x00000019
|
||||
DIF_NEWDEVICEWIZARD_PRESELECT DI_FUNCTION = 0x0000001A
|
||||
DIF_NEWDEVICEWIZARD_SELECT DI_FUNCTION = 0x0000001B
|
||||
DIF_NEWDEVICEWIZARD_PREANALYZE DI_FUNCTION = 0x0000001C
|
||||
DIF_NEWDEVICEWIZARD_POSTANALYZE DI_FUNCTION = 0x0000001D
|
||||
DIF_NEWDEVICEWIZARD_FINISHINSTALL DI_FUNCTION = 0x0000001E
|
||||
DIF_INSTALLINTERFACES DI_FUNCTION = 0x00000020
|
||||
DIF_DETECTCANCEL DI_FUNCTION = 0x00000021
|
||||
DIF_REGISTER_COINSTALLERS DI_FUNCTION = 0x00000022
|
||||
DIF_ADDPROPERTYPAGE_ADVANCED DI_FUNCTION = 0x00000023
|
||||
DIF_ADDPROPERTYPAGE_BASIC DI_FUNCTION = 0x00000024
|
||||
DIF_TROUBLESHOOTER DI_FUNCTION = 0x00000026
|
||||
DIF_POWERMESSAGEWAKE DI_FUNCTION = 0x00000027
|
||||
DIF_ADDREMOTEPROPERTYPAGE_ADVANCED DI_FUNCTION = 0x00000028
|
||||
DIF_UPDATEDRIVER_UI DI_FUNCTION = 0x00000029
|
||||
DIF_FINISHINSTALL_ACTION DI_FUNCTION = 0x0000002A
|
||||
)
|
||||
|
||||
// DevInstallParams is device installation parameters structure (associated with a particular device information element, or globally with a device information set)
|
||||
type DevInstallParams struct {
|
||||
size uint32
|
||||
Flags DI_FLAGS
|
||||
FlagsEx DI_FLAGSEX
|
||||
hwndParent uintptr
|
||||
InstallMsgHandler uintptr
|
||||
InstallMsgHandlerContext uintptr
|
||||
FileQueue HSPFILEQ
|
||||
_ uintptr
|
||||
_ uint32
|
||||
driverPath [windows.MAX_PATH]uint16
|
||||
}
|
||||
|
||||
func (params *DevInstallParams) GetDriverPath() string {
|
||||
return windows.UTF16ToString(params.driverPath[:])
|
||||
}
|
||||
|
||||
func (params *DevInstallParams) SetDriverPath(driverPath string) error {
|
||||
str, err := syscall.UTF16FromString(driverPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(params.driverPath[:], str)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DI_FLAGS is SP_DEVINSTALL_PARAMS.Flags values
|
||||
type DI_FLAGS uint32
|
||||
|
||||
const (
|
||||
// Flags for choosing a device
|
||||
DI_SHOWOEM DI_FLAGS = 0x00000001 // support Other... button
|
||||
DI_SHOWCOMPAT DI_FLAGS = 0x00000002 // show compatibility list
|
||||
DI_SHOWCLASS DI_FLAGS = 0x00000004 // show class list
|
||||
DI_SHOWALL DI_FLAGS = 0x00000007 // both class & compat list shown
|
||||
DI_NOVCP DI_FLAGS = 0x00000008 // don't create a new copy queue--use caller-supplied FileQueue
|
||||
DI_DIDCOMPAT DI_FLAGS = 0x00000010 // Searched for compatible devices
|
||||
DI_DIDCLASS DI_FLAGS = 0x00000020 // Searched for class devices
|
||||
DI_AUTOASSIGNRES DI_FLAGS = 0x00000040 // No UI for resources if possible
|
||||
|
||||
// Flags returned by DiInstallDevice to indicate need to reboot/restart
|
||||
DI_NEEDRESTART DI_FLAGS = 0x00000080 // Reboot required to take effect
|
||||
DI_NEEDREBOOT DI_FLAGS = 0x00000100 // ""
|
||||
|
||||
// Flags for device installation
|
||||
DI_NOBROWSE DI_FLAGS = 0x00000200 // no Browse... in InsertDisk
|
||||
|
||||
// Flags set by DiBuildDriverInfoList
|
||||
DI_MULTMFGS DI_FLAGS = 0x00000400 // Set if multiple manufacturers in class driver list
|
||||
|
||||
// Flag indicates that device is disabled
|
||||
DI_DISABLED DI_FLAGS = 0x00000800 // Set if device disabled
|
||||
|
||||
// Flags for Device/Class Properties
|
||||
DI_GENERALPAGE_ADDED DI_FLAGS = 0x00001000
|
||||
DI_RESOURCEPAGE_ADDED DI_FLAGS = 0x00002000
|
||||
|
||||
// Flag to indicate the setting properties for this Device (or class) caused a change so the Dev Mgr UI probably needs to be updated.
|
||||
DI_PROPERTIES_CHANGE DI_FLAGS = 0x00004000
|
||||
|
||||
// Flag to indicate that the sorting from the INF file should be used.
|
||||
DI_INF_IS_SORTED DI_FLAGS = 0x00008000
|
||||
|
||||
// Flag to indicate that only the the INF specified by SP_DEVINSTALL_PARAMS.DriverPath should be searched.
|
||||
DI_ENUMSINGLEINF DI_FLAGS = 0x00010000
|
||||
|
||||
// Flag that prevents ConfigMgr from removing/re-enumerating devices during device
|
||||
// registration, installation, and deletion.
|
||||
DI_DONOTCALLCONFIGMG DI_FLAGS = 0x00020000
|
||||
|
||||
// The following flag can be used to install a device disabled
|
||||
DI_INSTALLDISABLED DI_FLAGS = 0x00040000
|
||||
|
||||
// Flag that causes SetupDiBuildDriverInfoList to build a device's compatible driver
|
||||
// list from its existing class driver list, instead of the normal INF search.
|
||||
DI_COMPAT_FROM_CLASS DI_FLAGS = 0x00080000
|
||||
|
||||
// This flag is set if the Class Install params should be used.
|
||||
DI_CLASSINSTALLPARAMS DI_FLAGS = 0x00100000
|
||||
|
||||
// This flag is set if the caller of DiCallClassInstaller does NOT want the internal default action performed if the Class installer returns ERROR_DI_DO_DEFAULT.
|
||||
DI_NODI_DEFAULTACTION DI_FLAGS = 0x00200000
|
||||
|
||||
// Flags for device installation
|
||||
DI_QUIETINSTALL DI_FLAGS = 0x00800000 // don't confuse the user with questions or excess info
|
||||
DI_NOFILECOPY DI_FLAGS = 0x01000000 // No file Copy necessary
|
||||
DI_FORCECOPY DI_FLAGS = 0x02000000 // Force files to be copied from install path
|
||||
DI_DRIVERPAGE_ADDED DI_FLAGS = 0x04000000 // Prop provider added Driver page.
|
||||
DI_USECI_SELECTSTRINGS DI_FLAGS = 0x08000000 // Use Class Installer Provided strings in the Select Device Dlg
|
||||
DI_OVERRIDE_INFFLAGS DI_FLAGS = 0x10000000 // Override INF flags
|
||||
DI_PROPS_NOCHANGEUSAGE DI_FLAGS = 0x20000000 // No Enable/Disable in General Props
|
||||
|
||||
DI_NOSELECTICONS DI_FLAGS = 0x40000000 // No small icons in select device dialogs
|
||||
|
||||
DI_NOWRITE_IDS DI_FLAGS = 0x80000000 // Don't write HW & Compat IDs on install
|
||||
)
|
||||
|
||||
// DI_FLAGSEX is SP_DEVINSTALL_PARAMS.FlagsEx values
|
||||
type DI_FLAGSEX uint32
|
||||
|
||||
const (
|
||||
DI_FLAGSEX_CI_FAILED DI_FLAGSEX = 0x00000004 // Failed to Load/Call class installer
|
||||
DI_FLAGSEX_FINISHINSTALL_ACTION DI_FLAGSEX = 0x00000008 // Class/co-installer wants to get a DIF_FINISH_INSTALL action in client context.
|
||||
DI_FLAGSEX_DIDINFOLIST DI_FLAGSEX = 0x00000010 // Did the Class Info List
|
||||
DI_FLAGSEX_DIDCOMPATINFO DI_FLAGSEX = 0x00000020 // Did the Compat Info List
|
||||
DI_FLAGSEX_FILTERCLASSES DI_FLAGSEX = 0x00000040
|
||||
DI_FLAGSEX_SETFAILEDINSTALL DI_FLAGSEX = 0x00000080
|
||||
DI_FLAGSEX_DEVICECHANGE DI_FLAGSEX = 0x00000100
|
||||
DI_FLAGSEX_ALWAYSWRITEIDS DI_FLAGSEX = 0x00000200
|
||||
DI_FLAGSEX_PROPCHANGE_PENDING DI_FLAGSEX = 0x00000400 // One or more device property sheets have had changes made to them, and need to have a DIF_PROPERTYCHANGE occur.
|
||||
DI_FLAGSEX_ALLOWEXCLUDEDDRVS DI_FLAGSEX = 0x00000800
|
||||
DI_FLAGSEX_NOUIONQUERYREMOVE DI_FLAGSEX = 0x00001000
|
||||
DI_FLAGSEX_USECLASSFORCOMPAT DI_FLAGSEX = 0x00002000 // Use the device's class when building compat drv list. (Ignored if DI_COMPAT_FROM_CLASS flag is specified.)
|
||||
DI_FLAGSEX_NO_DRVREG_MODIFY DI_FLAGSEX = 0x00008000 // Don't run AddReg and DelReg for device's software (driver) key.
|
||||
DI_FLAGSEX_IN_SYSTEM_SETUP DI_FLAGSEX = 0x00010000 // Installation is occurring during initial system setup.
|
||||
DI_FLAGSEX_INET_DRIVER DI_FLAGSEX = 0x00020000 // Driver came from Windows Update
|
||||
DI_FLAGSEX_APPENDDRIVERLIST DI_FLAGSEX = 0x00040000 // Cause SetupDiBuildDriverInfoList to append a new driver list to an existing list.
|
||||
DI_FLAGSEX_PREINSTALLBACKUP DI_FLAGSEX = 0x00080000 // not used
|
||||
DI_FLAGSEX_BACKUPONREPLACE DI_FLAGSEX = 0x00100000 // not used
|
||||
DI_FLAGSEX_DRIVERLIST_FROM_URL DI_FLAGSEX = 0x00200000 // build driver list from INF(s) retrieved from URL specified in SP_DEVINSTALL_PARAMS.DriverPath (empty string means Windows Update website)
|
||||
DI_FLAGSEX_EXCLUDE_OLD_INET_DRIVERS DI_FLAGSEX = 0x00800000 // Don't include old Internet drivers when building a driver list. Ignored on Windows Vista and later.
|
||||
DI_FLAGSEX_POWERPAGE_ADDED DI_FLAGSEX = 0x01000000 // class installer added their own power page
|
||||
DI_FLAGSEX_FILTERSIMILARDRIVERS DI_FLAGSEX = 0x02000000 // only include similar drivers in class list
|
||||
DI_FLAGSEX_INSTALLEDDRIVER DI_FLAGSEX = 0x04000000 // only add the installed driver to the class or compat driver list. Used in calls to SetupDiBuildDriverInfoList
|
||||
DI_FLAGSEX_NO_CLASSLIST_NODE_MERGE DI_FLAGSEX = 0x08000000 // Don't remove identical driver nodes from the class list
|
||||
DI_FLAGSEX_ALTPLATFORM_DRVSEARCH DI_FLAGSEX = 0x10000000 // Build driver list based on alternate platform information specified in associated file queue
|
||||
DI_FLAGSEX_RESTART_DEVICE_ONLY DI_FLAGSEX = 0x20000000 // only restart the device drivers are being installed on as opposed to restarting all devices using those drivers.
|
||||
DI_FLAGSEX_RECURSIVESEARCH DI_FLAGSEX = 0x40000000 // Tell SetupDiBuildDriverInfoList to do a recursive search
|
||||
DI_FLAGSEX_SEARCH_PUBLISHED_INFS DI_FLAGSEX = 0x80000000 // Tell SetupDiBuildDriverInfoList to do a "published INF" search
|
||||
)
|
||||
|
||||
// ClassInstallHeader is the first member of any class install parameters structure. It contains the device installation request code that defines the format of the rest of the install parameters structure.
|
||||
type ClassInstallHeader struct {
|
||||
size uint32
|
||||
InstallFunction DI_FUNCTION
|
||||
}
|
||||
|
||||
func MakeClassInstallHeader(installFunction DI_FUNCTION) *ClassInstallHeader {
|
||||
hdr := &ClassInstallHeader{InstallFunction: installFunction}
|
||||
hdr.size = uint32(unsafe.Sizeof(*hdr))
|
||||
return hdr
|
||||
}
|
||||
|
||||
// DICS_FLAG specifies the scope of a device property change
|
||||
type DICS_FLAG uint32
|
||||
|
||||
const (
|
||||
DICS_FLAG_GLOBAL DICS_FLAG = 0x00000001 // make change in all hardware profiles
|
||||
DICS_FLAG_CONFIGSPECIFIC DICS_FLAG = 0x00000002 // make change in specified profile only
|
||||
DICS_FLAG_CONFIGGENERAL DICS_FLAG = 0x00000004 // 1 or more hardware profile-specific changes to follow
|
||||
)
|
||||
|
||||
// DI_REMOVEDEVICE specifies the scope of the device removal
|
||||
type DI_REMOVEDEVICE uint32
|
||||
|
||||
const (
|
||||
DI_REMOVEDEVICE_GLOBAL DI_REMOVEDEVICE = 0x00000001 // Make this change in all hardware profiles. Remove information about the device from the registry.
|
||||
DI_REMOVEDEVICE_CONFIGSPECIFIC DI_REMOVEDEVICE = 0x00000002 // Make this change to only the hardware profile specified by HwProfile. this flag only applies to root-enumerated devices. When Windows removes the device from the last hardware profile in which it was configured, Windows performs a global removal.
|
||||
)
|
||||
|
||||
// RemoveDeviceParams is a structure corresponding to a DIF_REMOVE install function.
|
||||
type RemoveDeviceParams struct {
|
||||
ClassInstallHeader ClassInstallHeader
|
||||
Scope DI_REMOVEDEVICE
|
||||
HwProfile uint32
|
||||
}
|
||||
|
||||
// DrvInfoData is driver information structure (member of a driver info list that may be associated with a particular device instance, or (globally) with a device information set)
|
||||
type DrvInfoData struct {
|
||||
size uint32
|
||||
DriverType uint32
|
||||
_ uintptr
|
||||
description [LINE_LEN]uint16
|
||||
mfgName [LINE_LEN]uint16
|
||||
providerName [LINE_LEN]uint16
|
||||
DriverDate windows.Filetime
|
||||
DriverVersion uint64
|
||||
}
|
||||
|
||||
func (data *DrvInfoData) GetDescription() string {
|
||||
return windows.UTF16ToString(data.description[:])
|
||||
}
|
||||
|
||||
func (data *DrvInfoData) SetDescription(description string) error {
|
||||
str, err := syscall.UTF16FromString(description)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(data.description[:], str)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (data *DrvInfoData) GetMfgName() string {
|
||||
return windows.UTF16ToString(data.mfgName[:])
|
||||
}
|
||||
|
||||
func (data *DrvInfoData) SetMfgName(mfgName string) error {
|
||||
str, err := syscall.UTF16FromString(mfgName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(data.mfgName[:], str)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (data *DrvInfoData) GetProviderName() string {
|
||||
return windows.UTF16ToString(data.providerName[:])
|
||||
}
|
||||
|
||||
func (data *DrvInfoData) SetProviderName(providerName string) error {
|
||||
str, err := syscall.UTF16FromString(providerName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(data.providerName[:], str)
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsNewer method returns true if DrvInfoData date and version is newer than supplied parameters.
|
||||
func (data *DrvInfoData) IsNewer(driverDate windows.Filetime, driverVersion uint64) bool {
|
||||
if data.DriverDate.HighDateTime > driverDate.HighDateTime {
|
||||
return true
|
||||
}
|
||||
if data.DriverDate.HighDateTime < driverDate.HighDateTime {
|
||||
return false
|
||||
}
|
||||
|
||||
if data.DriverDate.LowDateTime > driverDate.LowDateTime {
|
||||
return true
|
||||
}
|
||||
if data.DriverDate.LowDateTime < driverDate.LowDateTime {
|
||||
return false
|
||||
}
|
||||
|
||||
if data.DriverVersion > driverVersion {
|
||||
return true
|
||||
}
|
||||
if data.DriverVersion < driverVersion {
|
||||
return false
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// DrvInfoDetailData is driver information details structure (provides detailed information about a particular driver information structure)
|
||||
type DrvInfoDetailData struct {
|
||||
size uint32 // On input, this must be exactly the sizeof(DrvInfoDetailData). On output, we set this member to the actual size of structure data.
|
||||
InfDate windows.Filetime
|
||||
compatIDsOffset uint32
|
||||
compatIDsLength uint32
|
||||
_ uintptr
|
||||
sectionName [LINE_LEN]uint16
|
||||
infFileName [windows.MAX_PATH]uint16
|
||||
drvDescription [LINE_LEN]uint16
|
||||
hardwareID [1]uint16
|
||||
}
|
||||
|
||||
func (data *DrvInfoDetailData) GetSectionName() string {
|
||||
return windows.UTF16ToString(data.sectionName[:])
|
||||
}
|
||||
|
||||
func (data *DrvInfoDetailData) GetInfFileName() string {
|
||||
return windows.UTF16ToString(data.infFileName[:])
|
||||
}
|
||||
|
||||
func (data *DrvInfoDetailData) GetDrvDescription() string {
|
||||
return windows.UTF16ToString(data.drvDescription[:])
|
||||
}
|
||||
|
||||
func (data *DrvInfoDetailData) GetHardwareID() string {
|
||||
if data.compatIDsOffset > 1 {
|
||||
bufW := data.getBuf()
|
||||
return windows.UTF16ToString(bufW[:wcslen(bufW)])
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (data *DrvInfoDetailData) GetCompatIDs() []string {
|
||||
a := make([]string, 0)
|
||||
|
||||
if data.compatIDsLength > 0 {
|
||||
bufW := data.getBuf()
|
||||
bufW = bufW[data.compatIDsOffset : data.compatIDsOffset+data.compatIDsLength]
|
||||
for i := 0; i < len(bufW); {
|
||||
j := i + wcslen(bufW[i:])
|
||||
if i < j {
|
||||
a = append(a, windows.UTF16ToString(bufW[i:j]))
|
||||
}
|
||||
i = j + 1
|
||||
}
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
func (data *DrvInfoDetailData) getBuf() []uint16 {
|
||||
len := (data.size - uint32(unsafe.Offsetof(data.hardwareID))) / 2
|
||||
sl := struct {
|
||||
addr *uint16
|
||||
len int
|
||||
cap int
|
||||
}{&data.hardwareID[0], int(len), int(len)}
|
||||
return *(*[]uint16)(unsafe.Pointer(&sl))
|
||||
}
|
||||
|
||||
// IsCompatible method tests if given hardware ID matches the driver or is listed on the compatible ID list.
|
||||
func (data *DrvInfoDetailData) IsCompatible(hwid string) bool {
|
||||
hwidLC := strings.ToLower(hwid)
|
||||
if strings.ToLower(data.GetHardwareID()) == hwidLC {
|
||||
return true
|
||||
}
|
||||
a := data.GetCompatIDs()
|
||||
for i := range a {
|
||||
if strings.ToLower(a[i]) == hwidLC {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// DICD flags control SetupDiCreateDeviceInfo
|
||||
type DICD uint32
|
||||
|
||||
const (
|
||||
DICD_GENERATE_ID DICD = 0x00000001
|
||||
DICD_INHERIT_CLASSDRVS DICD = 0x00000002
|
||||
)
|
||||
|
||||
//
|
||||
// SPDIT flags to distinguish between class drivers and
|
||||
// device drivers.
|
||||
// (Passed in 'DriverType' parameter of driver information list APIs)
|
||||
//
|
||||
type SPDIT uint32
|
||||
|
||||
const (
|
||||
SPDIT_NODRIVER SPDIT = 0x00000000
|
||||
SPDIT_CLASSDRIVER SPDIT = 0x00000001
|
||||
SPDIT_COMPATDRIVER SPDIT = 0x00000002
|
||||
)
|
||||
|
||||
// DIGCF flags control what is included in the device information set built by SetupDiGetClassDevs
|
||||
type DIGCF uint32
|
||||
|
||||
const (
|
||||
DIGCF_DEFAULT DIGCF = 0x00000001 // only valid with DIGCF_DEVICEINTERFACE
|
||||
DIGCF_PRESENT DIGCF = 0x00000002
|
||||
DIGCF_ALLCLASSES DIGCF = 0x00000004
|
||||
DIGCF_PROFILE DIGCF = 0x00000008
|
||||
DIGCF_DEVICEINTERFACE DIGCF = 0x00000010
|
||||
)
|
||||
|
||||
// DIREG specifies values for SetupDiCreateDevRegKey, SetupDiOpenDevRegKey, and SetupDiDeleteDevRegKey.
|
||||
type DIREG uint32
|
||||
|
||||
const (
|
||||
DIREG_DEV DIREG = 0x00000001 // Open/Create/Delete device key
|
||||
DIREG_DRV DIREG = 0x00000002 // Open/Create/Delete driver key
|
||||
DIREG_BOTH DIREG = 0x00000004 // Delete both driver and Device key
|
||||
)
|
||||
|
||||
//
|
||||
// SPDRP specifies device registry property codes
|
||||
// (Codes marked as read-only (R) may only be used for
|
||||
// SetupDiGetDeviceRegistryProperty)
|
||||
//
|
||||
// These values should cover the same set of registry properties
|
||||
// as defined by the CM_DRP codes in cfgmgr32.h.
|
||||
//
|
||||
// Note that SPDRP codes are zero based while CM_DRP codes are one based!
|
||||
//
|
||||
type SPDRP uint32
|
||||
|
||||
const (
|
||||
SPDRP_DEVICEDESC SPDRP = 0x00000000 // DeviceDesc (R/W)
|
||||
SPDRP_HARDWAREID SPDRP = 0x00000001 // HardwareID (R/W)
|
||||
SPDRP_COMPATIBLEIDS SPDRP = 0x00000002 // CompatibleIDs (R/W)
|
||||
SPDRP_SERVICE SPDRP = 0x00000004 // Service (R/W)
|
||||
SPDRP_CLASS SPDRP = 0x00000007 // Class (R--tied to ClassGUID)
|
||||
SPDRP_CLASSGUID SPDRP = 0x00000008 // ClassGUID (R/W)
|
||||
SPDRP_DRIVER SPDRP = 0x00000009 // Driver (R/W)
|
||||
SPDRP_CONFIGFLAGS SPDRP = 0x0000000A // ConfigFlags (R/W)
|
||||
SPDRP_MFG SPDRP = 0x0000000B // Mfg (R/W)
|
||||
SPDRP_FRIENDLYNAME SPDRP = 0x0000000C // FriendlyName (R/W)
|
||||
SPDRP_LOCATION_INFORMATION SPDRP = 0x0000000D // LocationInformation (R/W)
|
||||
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME SPDRP = 0x0000000E // PhysicalDeviceObjectName (R)
|
||||
SPDRP_CAPABILITIES SPDRP = 0x0000000F // Capabilities (R)
|
||||
SPDRP_UI_NUMBER SPDRP = 0x00000010 // UiNumber (R)
|
||||
SPDRP_UPPERFILTERS SPDRP = 0x00000011 // UpperFilters (R/W)
|
||||
SPDRP_LOWERFILTERS SPDRP = 0x00000012 // LowerFilters (R/W)
|
||||
SPDRP_BUSTYPEGUID SPDRP = 0x00000013 // BusTypeGUID (R)
|
||||
SPDRP_LEGACYBUSTYPE SPDRP = 0x00000014 // LegacyBusType (R)
|
||||
SPDRP_BUSNUMBER SPDRP = 0x00000015 // BusNumber (R)
|
||||
SPDRP_ENUMERATOR_NAME SPDRP = 0x00000016 // Enumerator Name (R)
|
||||
SPDRP_SECURITY SPDRP = 0x00000017 // Security (R/W, binary form)
|
||||
SPDRP_SECURITY_SDS SPDRP = 0x00000018 // Security (W, SDS form)
|
||||
SPDRP_DEVTYPE SPDRP = 0x00000019 // Device Type (R/W)
|
||||
SPDRP_EXCLUSIVE SPDRP = 0x0000001A // Device is exclusive-access (R/W)
|
||||
SPDRP_CHARACTERISTICS SPDRP = 0x0000001B // Device Characteristics (R/W)
|
||||
SPDRP_ADDRESS SPDRP = 0x0000001C // Device Address (R)
|
||||
SPDRP_UI_NUMBER_DESC_FORMAT SPDRP = 0x0000001D // UiNumberDescFormat (R/W)
|
||||
SPDRP_DEVICE_POWER_DATA SPDRP = 0x0000001E // Device Power Data (R)
|
||||
SPDRP_REMOVAL_POLICY SPDRP = 0x0000001F // Removal Policy (R)
|
||||
SPDRP_REMOVAL_POLICY_HW_DEFAULT SPDRP = 0x00000020 // Hardware Removal Policy (R)
|
||||
SPDRP_REMOVAL_POLICY_OVERRIDE SPDRP = 0x00000021 // Removal Policy Override (RW)
|
||||
SPDRP_INSTALL_STATE SPDRP = 0x00000022 // Device Install State (R)
|
||||
SPDRP_LOCATION_PATHS SPDRP = 0x00000023 // Device Location Paths (R)
|
||||
SPDRP_BASE_CONTAINERID SPDRP = 0x00000024 // Base ContainerID (R)
|
||||
|
||||
SPDRP_MAXIMUM_PROPERTY SPDRP = 0x00000025 // Upper bound on ordinals
|
||||
)
|
||||
370
tun/wintun/setupapi/zsetupapi_windows.go
Normal file
370
tun/wintun/setupapi/zsetupapi_windows.go
Normal file
@@ -0,0 +1,370 @@
|
||||
// Code generated by 'go generate'; DO NOT EDIT.
|
||||
|
||||
package setupapi
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
// Do the interface allocations only once for common
|
||||
// Errno values.
|
||||
const (
|
||||
errnoERROR_IO_PENDING = 997
|
||||
)
|
||||
|
||||
var (
|
||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||
)
|
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e syscall.Errno) error {
|
||||
switch e {
|
||||
case 0:
|
||||
return nil
|
||||
case errnoERROR_IO_PENDING:
|
||||
return errERROR_IO_PENDING
|
||||
}
|
||||
// TODO: add more here, after collecting data on the common
|
||||
// error values see on Windows. (perhaps when running
|
||||
// all.bat?)
|
||||
return e
|
||||
}
|
||||
|
||||
var (
|
||||
modsetupapi = windows.NewLazySystemDLL("setupapi.dll")
|
||||
|
||||
procSetupDiCreateDeviceInfoListExW = modsetupapi.NewProc("SetupDiCreateDeviceInfoListExW")
|
||||
procSetupDiGetDeviceInfoListDetailW = modsetupapi.NewProc("SetupDiGetDeviceInfoListDetailW")
|
||||
procSetupDiCreateDeviceInfoW = modsetupapi.NewProc("SetupDiCreateDeviceInfoW")
|
||||
procSetupDiEnumDeviceInfo = modsetupapi.NewProc("SetupDiEnumDeviceInfo")
|
||||
procSetupDiDestroyDeviceInfoList = modsetupapi.NewProc("SetupDiDestroyDeviceInfoList")
|
||||
procSetupDiBuildDriverInfoList = modsetupapi.NewProc("SetupDiBuildDriverInfoList")
|
||||
procSetupDiCancelDriverInfoSearch = modsetupapi.NewProc("SetupDiCancelDriverInfoSearch")
|
||||
procSetupDiEnumDriverInfoW = modsetupapi.NewProc("SetupDiEnumDriverInfoW")
|
||||
procSetupDiGetSelectedDriverW = modsetupapi.NewProc("SetupDiGetSelectedDriverW")
|
||||
procSetupDiSetSelectedDriverW = modsetupapi.NewProc("SetupDiSetSelectedDriverW")
|
||||
procSetupDiGetDriverInfoDetailW = modsetupapi.NewProc("SetupDiGetDriverInfoDetailW")
|
||||
procSetupDiDestroyDriverInfoList = modsetupapi.NewProc("SetupDiDestroyDriverInfoList")
|
||||
procSetupDiGetClassDevsExW = modsetupapi.NewProc("SetupDiGetClassDevsExW")
|
||||
procSetupDiCallClassInstaller = modsetupapi.NewProc("SetupDiCallClassInstaller")
|
||||
procSetupDiOpenDevRegKey = modsetupapi.NewProc("SetupDiOpenDevRegKey")
|
||||
procSetupDiGetDeviceRegistryPropertyW = modsetupapi.NewProc("SetupDiGetDeviceRegistryPropertyW")
|
||||
procSetupDiSetDeviceRegistryPropertyW = modsetupapi.NewProc("SetupDiSetDeviceRegistryPropertyW")
|
||||
procSetupDiGetDeviceInstallParamsW = modsetupapi.NewProc("SetupDiGetDeviceInstallParamsW")
|
||||
procSetupDiGetClassInstallParamsW = modsetupapi.NewProc("SetupDiGetClassInstallParamsW")
|
||||
procSetupDiSetDeviceInstallParamsW = modsetupapi.NewProc("SetupDiSetDeviceInstallParamsW")
|
||||
procSetupDiSetClassInstallParamsW = modsetupapi.NewProc("SetupDiSetClassInstallParamsW")
|
||||
procSetupDiClassNameFromGuidExW = modsetupapi.NewProc("SetupDiClassNameFromGuidExW")
|
||||
procSetupDiClassGuidsFromNameExW = modsetupapi.NewProc("SetupDiClassGuidsFromNameExW")
|
||||
procSetupDiGetSelectedDevice = modsetupapi.NewProc("SetupDiGetSelectedDevice")
|
||||
procSetupDiSetSelectedDevice = modsetupapi.NewProc("SetupDiSetSelectedDevice")
|
||||
)
|
||||
|
||||
func setupDiCreateDeviceInfoListEx(classGUID *windows.GUID, hwndParent uintptr, machineName *uint16, reserved uintptr) (handle DevInfo, err error) {
|
||||
r0, _, e1 := syscall.Syscall6(procSetupDiCreateDeviceInfoListExW.Addr(), 4, uintptr(unsafe.Pointer(classGUID)), uintptr(hwndParent), uintptr(unsafe.Pointer(machineName)), uintptr(reserved), 0, 0)
|
||||
handle = DevInfo(r0)
|
||||
if handle == DevInfo(windows.InvalidHandle) {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiGetDeviceInfoListDetail(deviceInfoSet DevInfo, deviceInfoSetDetailData *DevInfoListDetailData) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiGetDeviceInfoListDetailW.Addr(), 2, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoSetDetailData)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiCreateDeviceInfo(deviceInfoSet DevInfo, DeviceName *uint16, classGUID *windows.GUID, DeviceDescription *uint16, hwndParent uintptr, CreationFlags DICD, deviceInfoData *DevInfoData) (err error) {
|
||||
r1, _, e1 := syscall.Syscall9(procSetupDiCreateDeviceInfoW.Addr(), 7, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(DeviceName)), uintptr(unsafe.Pointer(classGUID)), uintptr(unsafe.Pointer(DeviceDescription)), uintptr(hwndParent), uintptr(CreationFlags), uintptr(unsafe.Pointer(deviceInfoData)), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiEnumDeviceInfo(deviceInfoSet DevInfo, memberIndex uint32, deviceInfoData *DevInfoData) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiEnumDeviceInfo.Addr(), 3, uintptr(deviceInfoSet), uintptr(memberIndex), uintptr(unsafe.Pointer(deviceInfoData)))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetupDiDestroyDeviceInfoList(deviceInfoSet DevInfo) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiDestroyDeviceInfoList.Addr(), 1, uintptr(deviceInfoSet), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetupDiBuildDriverInfoList(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiBuildDriverInfoList.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(driverType))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetupDiCancelDriverInfoSearch(deviceInfoSet DevInfo) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiCancelDriverInfoSearch.Addr(), 1, uintptr(deviceInfoSet), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiEnumDriverInfo(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT, memberIndex uint32, driverInfoData *DrvInfoData) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procSetupDiEnumDriverInfoW.Addr(), 5, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(driverType), uintptr(memberIndex), uintptr(unsafe.Pointer(driverInfoData)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiGetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiGetSelectedDriverW.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(driverInfoData)))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetupDiSetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiSetSelectedDriverW.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(driverInfoData)))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiGetDriverInfoDetail(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData, driverInfoDetailData *DrvInfoDetailData, driverInfoDetailDataSize uint32, requiredSize *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procSetupDiGetDriverInfoDetailW.Addr(), 6, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(driverInfoData)), uintptr(unsafe.Pointer(driverInfoDetailData)), uintptr(driverInfoDetailDataSize), uintptr(unsafe.Pointer(requiredSize)))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetupDiDestroyDriverInfoList(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiDestroyDriverInfoList.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(driverType))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiGetClassDevsEx(classGUID *windows.GUID, Enumerator *uint16, hwndParent uintptr, Flags DIGCF, deviceInfoSet DevInfo, machineName *uint16, reserved uintptr) (handle DevInfo, err error) {
|
||||
r0, _, e1 := syscall.Syscall9(procSetupDiGetClassDevsExW.Addr(), 7, uintptr(unsafe.Pointer(classGUID)), uintptr(unsafe.Pointer(Enumerator)), uintptr(hwndParent), uintptr(Flags), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(machineName)), uintptr(reserved), 0, 0)
|
||||
handle = DevInfo(r0)
|
||||
if handle == DevInfo(windows.InvalidHandle) {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetupDiCallClassInstaller(installFunction DI_FUNCTION, deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiCallClassInstaller.Addr(), 3, uintptr(installFunction), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiOpenDevRegKey(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, Scope DICS_FLAG, HwProfile uint32, KeyType DIREG, samDesired uint32) (key windows.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall6(procSetupDiOpenDevRegKey.Addr(), 6, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(Scope), uintptr(HwProfile), uintptr(KeyType), uintptr(samDesired))
|
||||
key = windows.Handle(r0)
|
||||
if key == windows.InvalidHandle {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiGetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP, propertyRegDataType *uint32, propertyBuffer *byte, propertyBufferSize uint32, requiredSize *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall9(procSetupDiGetDeviceRegistryPropertyW.Addr(), 7, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(property), uintptr(unsafe.Pointer(propertyRegDataType)), uintptr(unsafe.Pointer(propertyBuffer)), uintptr(propertyBufferSize), uintptr(unsafe.Pointer(requiredSize)), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiSetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP, propertyBuffer *byte, propertyBufferSize uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procSetupDiSetDeviceRegistryPropertyW.Addr(), 5, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(property), uintptr(unsafe.Pointer(propertyBuffer)), uintptr(propertyBufferSize), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiGetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, deviceInstallParams *DevInstallParams) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiGetDeviceInstallParamsW.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(deviceInstallParams)))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetupDiGetClassInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32, requiredSize *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procSetupDiGetClassInstallParamsW.Addr(), 5, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(classInstallParams)), uintptr(classInstallParamsSize), uintptr(unsafe.Pointer(requiredSize)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetupDiSetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, deviceInstallParams *DevInstallParams) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiSetDeviceInstallParamsW.Addr(), 3, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(deviceInstallParams)))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetupDiSetClassInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procSetupDiSetClassInstallParamsW.Addr(), 4, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(classInstallParams)), uintptr(classInstallParamsSize), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiClassNameFromGuidEx(classGUID *windows.GUID, className *uint16, classNameSize uint32, requiredSize *uint32, machineName *uint16, reserved uintptr) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procSetupDiClassNameFromGuidExW.Addr(), 6, uintptr(unsafe.Pointer(classGUID)), uintptr(unsafe.Pointer(className)), uintptr(classNameSize), uintptr(unsafe.Pointer(requiredSize)), uintptr(unsafe.Pointer(machineName)), uintptr(reserved))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiClassGuidsFromNameEx(className *uint16, classGuidList *windows.GUID, classGuidListSize uint32, requiredSize *uint32, machineName *uint16, reserved uintptr) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procSetupDiClassGuidsFromNameExW.Addr(), 6, uintptr(unsafe.Pointer(className)), uintptr(unsafe.Pointer(classGuidList)), uintptr(classGuidListSize), uintptr(unsafe.Pointer(requiredSize)), uintptr(unsafe.Pointer(machineName)), uintptr(reserved))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupDiGetSelectedDevice(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiGetSelectedDevice.Addr(), 2, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SetupDiSetSelectedDevice(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetupDiSetSelectedDevice.Addr(), 2, uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
20
tun/wintun/setupapi/zsetupapi_windows_test.go
Normal file
20
tun/wintun/setupapi/zsetupapi_windows_test.go
Normal file
@@ -0,0 +1,20 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package setupapi
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func TestSetupDiDestroyDeviceInfoList(t *testing.T) {
|
||||
err := SetupDiDestroyDeviceInfoList(DevInfo(windows.InvalidHandle))
|
||||
if errWin, ok := err.(syscall.Errno); !ok || errWin != 6 /*ERROR_INVALID_HANDLE*/ {
|
||||
t.Errorf("SetupDiDestroyDeviceInfoList(nil, ...) should fail with ERROR_INVALID_HANDLE")
|
||||
}
|
||||
}
|
||||
510
tun/wintun/wintun_windows.go
Normal file
510
tun/wintun/wintun_windows.go
Normal file
@@ -0,0 +1,510 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package wintun
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"golang.zx2c4.com/wireguard/tun/wintun/netshell"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
"golang.zx2c4.com/wireguard/tun/wintun/guid"
|
||||
"golang.zx2c4.com/wireguard/tun/wintun/setupapi"
|
||||
)
|
||||
|
||||
//
|
||||
// Wintun is a handle of a Wintun adapter
|
||||
//
|
||||
type Wintun struct {
|
||||
CfgInstanceID windows.GUID
|
||||
LUIDIndex uint32
|
||||
IfType uint32
|
||||
}
|
||||
|
||||
var deviceClassNetGUID = windows.GUID{Data1: 0x4d36e972, Data2: 0xe325, Data3: 0x11ce, Data4: [8]byte{0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18}}
|
||||
|
||||
const hardwareID = "Wintun"
|
||||
const enumerator = ""
|
||||
const machineName = ""
|
||||
|
||||
//
|
||||
// MakeWintun creates interface handle and populates it from device registry key
|
||||
//
|
||||
func MakeWintun(deviceInfoSet setupapi.DevInfo, deviceInfoData *setupapi.DevInfoData) (*Wintun, error) {
|
||||
// Open HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\<class>\<id> registry key.
|
||||
key, err := deviceInfoSet.OpenDevRegKey(deviceInfoData, setupapi.DICS_FLAG_GLOBAL, 0, setupapi.DIREG_DRV, registry.READ)
|
||||
if err != nil {
|
||||
return nil, errors.New("Device-specific registry key open failed: " + err.Error())
|
||||
}
|
||||
defer key.Close()
|
||||
|
||||
var valueStr string
|
||||
var valueType uint32
|
||||
|
||||
// Read the NetCfgInstanceId value.
|
||||
valueStr, valueType, err = keyGetStringValueRetry(key, "NetCfgInstanceId")
|
||||
if err != nil {
|
||||
return nil, errors.New("RegQueryStringValue(\"NetCfgInstanceId\") failed: " + err.Error())
|
||||
}
|
||||
if valueType != registry.SZ {
|
||||
return nil, fmt.Errorf("NetCfgInstanceId registry value is not REG_SZ (expected: %v, provided: %v)", registry.SZ, valueType)
|
||||
}
|
||||
|
||||
// Convert to windows.GUID.
|
||||
ifid, err := guid.FromString(valueStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("NetCfgInstanceId registry value is not a GUID (expected: \"{...}\", provided: %q)", valueStr)
|
||||
}
|
||||
|
||||
// Read the NetLuidIndex value.
|
||||
luidIdx, valueType, err := key.GetIntegerValue("NetLuidIndex")
|
||||
if err != nil {
|
||||
return nil, errors.New("RegQueryValue(\"NetLuidIndex\") failed: " + err.Error())
|
||||
}
|
||||
|
||||
// Read the NetLuidIndex value.
|
||||
ifType, valueType, err := key.GetIntegerValue("*IfType")
|
||||
if err != nil {
|
||||
return nil, errors.New("RegQueryValue(\"*IfType\") failed: " + err.Error())
|
||||
}
|
||||
|
||||
return &Wintun{
|
||||
CfgInstanceID: *ifid,
|
||||
LUIDIndex: uint32(luidIdx),
|
||||
IfType: uint32(ifType),
|
||||
}, nil
|
||||
}
|
||||
|
||||
//
|
||||
// GetInterface finds interface by name.
|
||||
//
|
||||
// hwndParent is a handle to the top-level window to use for any user
|
||||
// interface that is related to non-device-specific actions (such as a select-
|
||||
// device dialog box that uses the global class driver list). This handle is
|
||||
// optional and can be 0. If a specific top-level window is not required, set
|
||||
// hwndParent to 0.
|
||||
//
|
||||
// Function returns interface if found, or nil otherwise. If the interface is
|
||||
// found but not Wintun-class, the function returns interface and an error.
|
||||
//
|
||||
func GetInterface(ifname string, hwndParent uintptr) (*Wintun, error) {
|
||||
// Create a list of network devices.
|
||||
devInfoList, err := setupapi.SetupDiGetClassDevsEx(&deviceClassNetGUID, enumerator, hwndParent, setupapi.DIGCF_PRESENT, setupapi.DevInfo(0), machineName)
|
||||
if err != nil {
|
||||
return nil, errors.New(fmt.Sprintf("SetupDiGetClassDevsEx(%v) failed: ", guid.ToString(&deviceClassNetGUID)) + err.Error())
|
||||
}
|
||||
defer devInfoList.Close()
|
||||
|
||||
// Windows requires each interface to have a different name. When
|
||||
// enforcing this, Windows treats interface names case-insensitive. If an
|
||||
// interface "FooBar" exists and this function reports there is no
|
||||
// interface "foobar", an attempt to create a new interface and name it
|
||||
// "foobar" would cause conflict with "FooBar".
|
||||
ifname = strings.ToLower(ifname)
|
||||
|
||||
for index := 0; ; index++ {
|
||||
// Get the device from the list. Should anything be wrong with this device, continue with next.
|
||||
deviceData, err := devInfoList.EnumDeviceInfo(index)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Get interface ID.
|
||||
wintun, err := MakeWintun(devInfoList, deviceData)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get interface name.
|
||||
ifname2, err := wintun.GetInterfaceName()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if ifname == strings.ToLower(ifname2) {
|
||||
// Interface name found. Check its driver.
|
||||
const driverType = setupapi.SPDIT_COMPATDRIVER
|
||||
err = devInfoList.BuildDriverInfoList(deviceData, driverType)
|
||||
if err != nil {
|
||||
return nil, errors.New("SetupDiBuildDriverInfoList failed: " + err.Error())
|
||||
}
|
||||
defer devInfoList.DestroyDriverInfoList(deviceData, driverType)
|
||||
|
||||
for index := 0; ; index++ {
|
||||
// Get a driver from the list.
|
||||
driverData, err := devInfoList.EnumDriverInfo(deviceData, driverType, index)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
|
||||
break
|
||||
}
|
||||
// Something is wrong with this driver. Skip it.
|
||||
continue
|
||||
}
|
||||
|
||||
// Get driver info details.
|
||||
driverDetailData, err := devInfoList.GetDriverInfoDetail(deviceData, driverData)
|
||||
if err != nil {
|
||||
// Something is wrong with this driver. Skip it.
|
||||
continue
|
||||
}
|
||||
|
||||
if driverDetailData.IsCompatible(hardwareID) {
|
||||
// Matching hardware ID found.
|
||||
return wintun, nil
|
||||
}
|
||||
}
|
||||
|
||||
// This interface is not using Wintun driver.
|
||||
return nil, errors.New("Foreign network interface with the same name exists")
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
//
|
||||
// CreateInterface creates a TUN interface.
|
||||
//
|
||||
// description is a string that supplies the text description of the device.
|
||||
// description is optional and can be "".
|
||||
//
|
||||
// hwndParent is a handle to the top-level window to use for any user
|
||||
// interface that is related to non-device-specific actions (such as a select-
|
||||
// device dialog box that uses the global class driver list). This handle is
|
||||
// optional and can be 0. If a specific top-level window is not required, set
|
||||
// hwndParent to 0.
|
||||
//
|
||||
// Function returns the network interface ID and a flag if reboot is required.
|
||||
//
|
||||
func CreateInterface(description string, hwndParent uintptr) (*Wintun, bool, error) {
|
||||
// Create an empty device info set for network adapter device class.
|
||||
devInfoList, err := setupapi.SetupDiCreateDeviceInfoListEx(&deviceClassNetGUID, hwndParent, machineName)
|
||||
if err != nil {
|
||||
return nil, false, errors.New(fmt.Sprintf("SetupDiCreateDeviceInfoListEx(%v) failed: ", guid.ToString(&deviceClassNetGUID)) + err.Error())
|
||||
}
|
||||
|
||||
// Get the device class name from GUID.
|
||||
className, err := setupapi.SetupDiClassNameFromGuidEx(&deviceClassNetGUID, machineName)
|
||||
if err != nil {
|
||||
return nil, false, errors.New(fmt.Sprintf("SetupDiClassNameFromGuidEx(%v) failed: ", guid.ToString(&deviceClassNetGUID)) + err.Error())
|
||||
}
|
||||
|
||||
// Create a new device info element and add it to the device info set.
|
||||
deviceData, err := devInfoList.CreateDeviceInfo(className, &deviceClassNetGUID, description, hwndParent, setupapi.DICD_GENERATE_ID)
|
||||
if err != nil {
|
||||
return nil, false, errors.New("SetupDiCreateDeviceInfo failed: " + err.Error())
|
||||
}
|
||||
|
||||
// Set a device information element as the selected member of a device information set.
|
||||
err = devInfoList.SetSelectedDevice(deviceData)
|
||||
if err != nil {
|
||||
return nil, false, errors.New("SetupDiSetSelectedDevice failed: " + err.Error())
|
||||
}
|
||||
|
||||
// Set Plug&Play device hardware ID property.
|
||||
hwid, err := syscall.UTF16FromString(hardwareID)
|
||||
if err != nil {
|
||||
return nil, false, err // syscall.UTF16FromString(hardwareID) should never fail: hardwareID is const string without NUL chars.
|
||||
}
|
||||
err = devInfoList.SetDeviceRegistryProperty(deviceData, setupapi.SPDRP_HARDWAREID, setupapi.UTF16ToBuf(append(hwid, 0)))
|
||||
if err != nil {
|
||||
return nil, false, errors.New("SetupDiSetDeviceRegistryProperty(SPDRP_HARDWAREID) failed: " + err.Error())
|
||||
}
|
||||
|
||||
// Search for the driver.
|
||||
const driverType = setupapi.SPDIT_CLASSDRIVER
|
||||
err = devInfoList.BuildDriverInfoList(deviceData, driverType)
|
||||
if err != nil {
|
||||
return nil, false, errors.New("SetupDiBuildDriverInfoList failed: " + err.Error())
|
||||
}
|
||||
defer devInfoList.DestroyDriverInfoList(deviceData, driverType)
|
||||
|
||||
driverDate := windows.Filetime{}
|
||||
driverVersion := uint64(0)
|
||||
for index := 0; ; index++ {
|
||||
// Get a driver from the list.
|
||||
driverData, err := devInfoList.EnumDriverInfo(deviceData, driverType, index)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
|
||||
break
|
||||
}
|
||||
// Something is wrong with this driver. Skip it.
|
||||
continue
|
||||
}
|
||||
|
||||
// Check the driver version first, since the check is trivial and will save us iterating over hardware IDs for any driver versioned prior our best match.
|
||||
if driverData.IsNewer(driverDate, driverVersion) {
|
||||
// Get driver info details.
|
||||
driverDetailData, err := devInfoList.GetDriverInfoDetail(deviceData, driverData)
|
||||
if err != nil {
|
||||
// Something is wrong with this driver. Skip it.
|
||||
continue
|
||||
}
|
||||
|
||||
if driverDetailData.IsCompatible(hardwareID) {
|
||||
// Matching hardware ID found. Select the driver.
|
||||
err := devInfoList.SetSelectedDriver(deviceData, driverData)
|
||||
if err != nil {
|
||||
// Something is wrong with this driver. Skip it.
|
||||
continue
|
||||
}
|
||||
|
||||
driverDate = driverData.DriverDate
|
||||
driverVersion = driverData.DriverVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if driverVersion == 0 {
|
||||
return nil, false, fmt.Errorf("No driver for device %q installed", hardwareID)
|
||||
}
|
||||
|
||||
// Call appropriate class installer.
|
||||
err = devInfoList.CallClassInstaller(setupapi.DIF_REGISTERDEVICE, deviceData)
|
||||
if err != nil {
|
||||
return nil, false, errors.New("SetupDiCallClassInstaller(DIF_REGISTERDEVICE) failed: " + err.Error())
|
||||
}
|
||||
|
||||
// Register device co-installers if any. (Ignore errors)
|
||||
devInfoList.CallClassInstaller(setupapi.DIF_REGISTER_COINSTALLERS, deviceData)
|
||||
|
||||
// Install interfaces if any. (Ignore errors)
|
||||
devInfoList.CallClassInstaller(setupapi.DIF_INSTALLINTERFACES, deviceData)
|
||||
|
||||
var wintun *Wintun
|
||||
var rebootRequired bool
|
||||
|
||||
// Install the device.
|
||||
err = devInfoList.CallClassInstaller(setupapi.DIF_INSTALLDEVICE, deviceData)
|
||||
if err != nil {
|
||||
err = errors.New("SetupDiCallClassInstaller(DIF_INSTALLDEVICE) failed: " + err.Error())
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
// Check if a system reboot is required. (Ignore errors)
|
||||
if ret, _ := checkReboot(devInfoList, deviceData); ret {
|
||||
rebootRequired = true
|
||||
}
|
||||
|
||||
// Get network interface. DIF_INSTALLDEVICE returns almost immediately, while the device
|
||||
// installation continues in the background. It might take a while, before all registry
|
||||
// keys and values are populated.
|
||||
for numAttempts := 0; numAttempts < 30; numAttempts++ {
|
||||
wintun, err = MakeWintun(devInfoList, deviceData)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == windows.ERROR_FILE_NOT_FOUND {
|
||||
// Wait and retry. TODO: Wait for a cancellable event instead.
|
||||
err = errors.New("Time-out waiting for adapter to get ready")
|
||||
time.Sleep(time.Second / 4)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return wintun, rebootRequired, nil
|
||||
}
|
||||
|
||||
// The interface failed to install, or the interface ID was unobtainable. Clean-up.
|
||||
removeDeviceParams := setupapi.RemoveDeviceParams{
|
||||
ClassInstallHeader: *setupapi.MakeClassInstallHeader(setupapi.DIF_REMOVE),
|
||||
Scope: setupapi.DI_REMOVEDEVICE_GLOBAL,
|
||||
}
|
||||
|
||||
// Set class installer parameters for DIF_REMOVE.
|
||||
if devInfoList.SetClassInstallParams(deviceData, &removeDeviceParams.ClassInstallHeader, uint32(unsafe.Sizeof(removeDeviceParams))) == nil {
|
||||
// Call appropriate class installer.
|
||||
if devInfoList.CallClassInstaller(setupapi.DIF_REMOVE, deviceData) == nil {
|
||||
// Check if a system reboot is required. (Ignore errors)
|
||||
if ret, _ := checkReboot(devInfoList, deviceData); ret {
|
||||
rebootRequired = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, rebootRequired, err
|
||||
}
|
||||
|
||||
//
|
||||
// DeleteInterface deletes a TUN interface.
|
||||
//
|
||||
// hwndParent is a handle to the top-level window to use for any user
|
||||
// interface that is related to non-device-specific actions (such as a select-
|
||||
// device dialog box that uses the global class driver list). This handle is
|
||||
// optional and can be 0. If a specific top-level window is not required, set
|
||||
// hwndParent to 0.
|
||||
//
|
||||
// Function returns true if the interface was found and deleted and a flag if
|
||||
// reboot is required.
|
||||
//
|
||||
func (wintun *Wintun) DeleteInterface(hwndParent uintptr) (bool, bool, error) {
|
||||
// Create a list of network devices.
|
||||
devInfoList, err := setupapi.SetupDiGetClassDevsEx(&deviceClassNetGUID, enumerator, hwndParent, setupapi.DIGCF_PRESENT, setupapi.DevInfo(0), machineName)
|
||||
if err != nil {
|
||||
return false, false, errors.New(fmt.Sprintf("SetupDiGetClassDevsEx(%v) failed: ", guid.ToString(&deviceClassNetGUID)) + err.Error())
|
||||
}
|
||||
defer devInfoList.Close()
|
||||
|
||||
// Iterate.
|
||||
for index := 0; ; index++ {
|
||||
// Get the device from the list. Should anything be wrong with this device, continue with next.
|
||||
deviceData, err := devInfoList.EnumDeviceInfo(index)
|
||||
if err != nil {
|
||||
if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Get interface ID.
|
||||
wintun2, err := MakeWintun(devInfoList, deviceData)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if wintun.CfgInstanceID == wintun2.CfgInstanceID {
|
||||
// Remove the device.
|
||||
removeDeviceParams := setupapi.RemoveDeviceParams{
|
||||
ClassInstallHeader: *setupapi.MakeClassInstallHeader(setupapi.DIF_REMOVE),
|
||||
Scope: setupapi.DI_REMOVEDEVICE_GLOBAL,
|
||||
}
|
||||
|
||||
// Set class installer parameters for DIF_REMOVE.
|
||||
err = devInfoList.SetClassInstallParams(deviceData, &removeDeviceParams.ClassInstallHeader, uint32(unsafe.Sizeof(removeDeviceParams)))
|
||||
if err != nil {
|
||||
return false, false, errors.New("SetupDiSetClassInstallParams failed: " + err.Error())
|
||||
}
|
||||
|
||||
// Call appropriate class installer.
|
||||
err = devInfoList.CallClassInstaller(setupapi.DIF_REMOVE, deviceData)
|
||||
if err != nil {
|
||||
return false, false, errors.New("SetupDiCallClassInstaller failed: " + err.Error())
|
||||
}
|
||||
|
||||
// Check if a system reboot is required. (Ignore errors)
|
||||
if ret, _ := checkReboot(devInfoList, deviceData); ret {
|
||||
return true, true, nil
|
||||
}
|
||||
|
||||
return true, false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, false, nil
|
||||
}
|
||||
|
||||
//
|
||||
// FlushInterface removes all properties from the interface and gives it only a very
|
||||
// vanilla IPv4 and IPv6 configuration with no addresses of any sort assigned.
|
||||
//
|
||||
func (wintun *Wintun) FlushInterface() error {
|
||||
//TODO: implement me!
|
||||
return nil
|
||||
}
|
||||
|
||||
//
|
||||
// checkReboot checks device install parameters if a system reboot is required.
|
||||
//
|
||||
func checkReboot(deviceInfoSet setupapi.DevInfo, deviceInfoData *setupapi.DevInfoData) (bool, error) {
|
||||
devInstallParams, err := deviceInfoSet.GetDeviceInstallParams(deviceInfoData)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if (devInstallParams.Flags & (setupapi.DI_NEEDREBOOT | setupapi.DI_NEEDRESTART)) != 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
//
|
||||
// GetInterfaceName returns network interface name.
|
||||
//
|
||||
func (wintun *Wintun) GetInterfaceName() (string, error) {
|
||||
key, err := registryOpenKeyRetry(registry.LOCAL_MACHINE, wintun.GetNetRegKeyName(), registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
return "", errors.New("Network-specific registry key open failed: " + err.Error())
|
||||
}
|
||||
defer key.Close()
|
||||
|
||||
// Get the interface name.
|
||||
return getRegStringValue(key, "Name")
|
||||
}
|
||||
|
||||
//
|
||||
// SetInterfaceName sets network interface name.
|
||||
//
|
||||
func (wintun *Wintun) SetInterfaceName(ifname string) error {
|
||||
// We open the registry key before calling HrRename, because the registry open will wait until the key exists.
|
||||
key, err := registryOpenKeyRetry(registry.LOCAL_MACHINE, wintun.GetNetRegKeyName(), registry.SET_VALUE)
|
||||
if err != nil {
|
||||
return errors.New("Network-specific registry key open failed: " + err.Error())
|
||||
}
|
||||
defer key.Close()
|
||||
|
||||
// We have to tell the various runtime COM services about the new name too. We ignore the
|
||||
// error because netshell isn't available on servercore.
|
||||
// TODO: netsh.exe falls back to NciSetConnection in this case. If somebody complains, maybe
|
||||
// we should do the same.
|
||||
_ = netshell.HrRenameConnection(&wintun.CfgInstanceID, windows.StringToUTF16Ptr(ifname))
|
||||
|
||||
// Set the interface name. The above line should have done this too, but in case it failed, we force it.
|
||||
return key.SetStringValue("Name", ifname)
|
||||
}
|
||||
|
||||
//
|
||||
// GetNetRegKeyName returns interface-specific network registry key name.
|
||||
//
|
||||
func (wintun *Wintun) GetNetRegKeyName() string {
|
||||
return fmt.Sprintf("SYSTEM\\CurrentControlSet\\Control\\Network\\%v\\%v\\Connection", guid.ToString(&deviceClassNetGUID), guid.ToString(&wintun.CfgInstanceID))
|
||||
}
|
||||
|
||||
//
|
||||
// getRegStringValue function reads a string value from registry.
|
||||
//
|
||||
// If the value type is REG_EXPAND_SZ the environment variables are expanded.
|
||||
// Should expanding fail, original string value and nil error are returned.
|
||||
//
|
||||
func getRegStringValue(key registry.Key, name string) (string, error) {
|
||||
// Read string value.
|
||||
value, valueType, err := keyGetStringValueRetry(key, name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if valueType != registry.EXPAND_SZ {
|
||||
// Value does not require expansion.
|
||||
return value, nil
|
||||
}
|
||||
|
||||
valueExp, err := registry.ExpandString(value)
|
||||
if err != nil {
|
||||
// Expanding failed: return original sting value.
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// Return expanded value.
|
||||
return valueExp, nil
|
||||
}
|
||||
|
||||
//
|
||||
// DataFileName returns Wintun device data pipe name.
|
||||
//
|
||||
func (wintun *Wintun) DataFileName() string {
|
||||
return fmt.Sprintf("\\\\.\\Global\\WINTUN%d", wintun.LUIDIndex)
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
package main
|
||||
const WireGuardGoVersion = "0.0.20180524"
|
||||
@@ -1,171 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2016 Andreas Auernhammer. All Rights Reserved.
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package xchacha20poly1305
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
)
|
||||
|
||||
func hChaCha20(out *[32]byte, nonce []byte, key *[32]byte) {
|
||||
|
||||
v00 := uint32(0x61707865)
|
||||
v01 := uint32(0x3320646e)
|
||||
v02 := uint32(0x79622d32)
|
||||
v03 := uint32(0x6b206574)
|
||||
|
||||
v04 := binary.LittleEndian.Uint32(key[0:])
|
||||
v05 := binary.LittleEndian.Uint32(key[4:])
|
||||
v06 := binary.LittleEndian.Uint32(key[8:])
|
||||
v07 := binary.LittleEndian.Uint32(key[12:])
|
||||
v08 := binary.LittleEndian.Uint32(key[16:])
|
||||
v09 := binary.LittleEndian.Uint32(key[20:])
|
||||
v10 := binary.LittleEndian.Uint32(key[24:])
|
||||
v11 := binary.LittleEndian.Uint32(key[28:])
|
||||
v12 := binary.LittleEndian.Uint32(nonce[0:])
|
||||
v13 := binary.LittleEndian.Uint32(nonce[4:])
|
||||
v14 := binary.LittleEndian.Uint32(nonce[8:])
|
||||
v15 := binary.LittleEndian.Uint32(nonce[12:])
|
||||
|
||||
for i := 0; i < 20; i += 2 {
|
||||
v00 += v04
|
||||
v12 ^= v00
|
||||
v12 = (v12 << 16) | (v12 >> 16)
|
||||
v08 += v12
|
||||
v04 ^= v08
|
||||
v04 = (v04 << 12) | (v04 >> 20)
|
||||
v00 += v04
|
||||
v12 ^= v00
|
||||
v12 = (v12 << 8) | (v12 >> 24)
|
||||
v08 += v12
|
||||
v04 ^= v08
|
||||
v04 = (v04 << 7) | (v04 >> 25)
|
||||
v01 += v05
|
||||
v13 ^= v01
|
||||
v13 = (v13 << 16) | (v13 >> 16)
|
||||
v09 += v13
|
||||
v05 ^= v09
|
||||
v05 = (v05 << 12) | (v05 >> 20)
|
||||
v01 += v05
|
||||
v13 ^= v01
|
||||
v13 = (v13 << 8) | (v13 >> 24)
|
||||
v09 += v13
|
||||
v05 ^= v09
|
||||
v05 = (v05 << 7) | (v05 >> 25)
|
||||
v02 += v06
|
||||
v14 ^= v02
|
||||
v14 = (v14 << 16) | (v14 >> 16)
|
||||
v10 += v14
|
||||
v06 ^= v10
|
||||
v06 = (v06 << 12) | (v06 >> 20)
|
||||
v02 += v06
|
||||
v14 ^= v02
|
||||
v14 = (v14 << 8) | (v14 >> 24)
|
||||
v10 += v14
|
||||
v06 ^= v10
|
||||
v06 = (v06 << 7) | (v06 >> 25)
|
||||
v03 += v07
|
||||
v15 ^= v03
|
||||
v15 = (v15 << 16) | (v15 >> 16)
|
||||
v11 += v15
|
||||
v07 ^= v11
|
||||
v07 = (v07 << 12) | (v07 >> 20)
|
||||
v03 += v07
|
||||
v15 ^= v03
|
||||
v15 = (v15 << 8) | (v15 >> 24)
|
||||
v11 += v15
|
||||
v07 ^= v11
|
||||
v07 = (v07 << 7) | (v07 >> 25)
|
||||
v00 += v05
|
||||
v15 ^= v00
|
||||
v15 = (v15 << 16) | (v15 >> 16)
|
||||
v10 += v15
|
||||
v05 ^= v10
|
||||
v05 = (v05 << 12) | (v05 >> 20)
|
||||
v00 += v05
|
||||
v15 ^= v00
|
||||
v15 = (v15 << 8) | (v15 >> 24)
|
||||
v10 += v15
|
||||
v05 ^= v10
|
||||
v05 = (v05 << 7) | (v05 >> 25)
|
||||
v01 += v06
|
||||
v12 ^= v01
|
||||
v12 = (v12 << 16) | (v12 >> 16)
|
||||
v11 += v12
|
||||
v06 ^= v11
|
||||
v06 = (v06 << 12) | (v06 >> 20)
|
||||
v01 += v06
|
||||
v12 ^= v01
|
||||
v12 = (v12 << 8) | (v12 >> 24)
|
||||
v11 += v12
|
||||
v06 ^= v11
|
||||
v06 = (v06 << 7) | (v06 >> 25)
|
||||
v02 += v07
|
||||
v13 ^= v02
|
||||
v13 = (v13 << 16) | (v13 >> 16)
|
||||
v08 += v13
|
||||
v07 ^= v08
|
||||
v07 = (v07 << 12) | (v07 >> 20)
|
||||
v02 += v07
|
||||
v13 ^= v02
|
||||
v13 = (v13 << 8) | (v13 >> 24)
|
||||
v08 += v13
|
||||
v07 ^= v08
|
||||
v07 = (v07 << 7) | (v07 >> 25)
|
||||
v03 += v04
|
||||
v14 ^= v03
|
||||
v14 = (v14 << 16) | (v14 >> 16)
|
||||
v09 += v14
|
||||
v04 ^= v09
|
||||
v04 = (v04 << 12) | (v04 >> 20)
|
||||
v03 += v04
|
||||
v14 ^= v03
|
||||
v14 = (v14 << 8) | (v14 >> 24)
|
||||
v09 += v14
|
||||
v04 ^= v09
|
||||
v04 = (v04 << 7) | (v04 >> 25)
|
||||
}
|
||||
|
||||
binary.LittleEndian.PutUint32(out[0:], v00)
|
||||
binary.LittleEndian.PutUint32(out[4:], v01)
|
||||
binary.LittleEndian.PutUint32(out[8:], v02)
|
||||
binary.LittleEndian.PutUint32(out[12:], v03)
|
||||
binary.LittleEndian.PutUint32(out[16:], v12)
|
||||
binary.LittleEndian.PutUint32(out[20:], v13)
|
||||
binary.LittleEndian.PutUint32(out[24:], v14)
|
||||
binary.LittleEndian.PutUint32(out[28:], v15)
|
||||
}
|
||||
|
||||
func Encrypt(
|
||||
dst []byte,
|
||||
nonceFull *[24]byte,
|
||||
plaintext []byte,
|
||||
additionalData []byte,
|
||||
key *[chacha20poly1305.KeySize]byte,
|
||||
) []byte {
|
||||
var nonce [chacha20poly1305.NonceSize]byte
|
||||
var derivedKey [chacha20poly1305.KeySize]byte
|
||||
hChaCha20(&derivedKey, nonceFull[:16], key)
|
||||
aead, _ := chacha20poly1305.New(derivedKey[:])
|
||||
copy(nonce[4:], nonceFull[16:])
|
||||
return aead.Seal(dst, nonce[:], plaintext, additionalData)
|
||||
}
|
||||
|
||||
func Decrypt(
|
||||
dst []byte,
|
||||
nonceFull *[24]byte,
|
||||
plaintext []byte,
|
||||
additionalData []byte,
|
||||
key *[chacha20poly1305.KeySize]byte,
|
||||
) ([]byte, error) {
|
||||
var nonce [chacha20poly1305.NonceSize]byte
|
||||
var derivedKey [chacha20poly1305.KeySize]byte
|
||||
hChaCha20(&derivedKey, nonceFull[:16], key)
|
||||
aead, _ := chacha20poly1305.New(derivedKey[:])
|
||||
copy(nonce[4:], nonceFull[16:])
|
||||
return aead.Open(dst, nonce[:], plaintext, additionalData)
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* Copyright (C) 2017-2018 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package xchacha20poly1305
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type XChaCha20Test struct {
|
||||
Nonce string
|
||||
Key string
|
||||
PT string
|
||||
CT string
|
||||
}
|
||||
|
||||
func TestXChaCha20(t *testing.T) {
|
||||
|
||||
tests := []XChaCha20Test{
|
||||
{
|
||||
Nonce: "000000000000000000000000000000000000000000000000",
|
||||
Key: "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
PT: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
CT: "789e9689e5208d7fd9e1f3c5b5341f48ef18a13e418998addadd97a3693a987f8e82ecd5c1433bfed1af49750c0f1ff29c4174a05b119aa3a9e8333812e0c0feb1299c5949d895ee01dbf50f8395dd84",
|
||||
},
|
||||
{
|
||||
Nonce: "0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f",
|
||||
Key: "0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f",
|
||||
PT: "0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f",
|
||||
CT: "e1a046aa7f71e2af8b80b6408b2fd8d3a350278cde79c94d9efaa475e1339b3dd490127b",
|
||||
},
|
||||
{
|
||||
Nonce: "d9a8213e8a697508805c2c171ad54487ead9e3e02d82d5bc",
|
||||
Key: "979196dbd78526f2f584f7534db3f5824d8ccfa858ca7e09bdd3656ecd36033c",
|
||||
PT: "43cc6d624e451bbed952c3e071dc6c03392ce11eb14316a94b2fdc98b22fedea",
|
||||
CT: "53c1e8bef2dbb8f2505ec010a7afe21d5a8e6dd8f987e4ea1a2ed5dfbc844ea400db34496fd2153526c6e87c36694200",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
nonce, err := hex.DecodeString(test.Nonce)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
key, err := hex.DecodeString(test.Key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
pt, err := hex.DecodeString(test.PT)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
func() {
|
||||
var nonceArray [24]byte
|
||||
var keyArray [32]byte
|
||||
copy(nonceArray[:], nonce)
|
||||
copy(keyArray[:], key)
|
||||
|
||||
// test encryption
|
||||
|
||||
ct := Encrypt(
|
||||
nil,
|
||||
&nonceArray,
|
||||
pt,
|
||||
nil,
|
||||
&keyArray,
|
||||
)
|
||||
ctHex := hex.EncodeToString(ct)
|
||||
if ctHex != test.CT {
|
||||
t.Fatal("encryption failed, expected:", test.CT, "got", ctHex)
|
||||
}
|
||||
|
||||
// test decryption
|
||||
|
||||
ptp, err := Decrypt(
|
||||
nil,
|
||||
&nonceArray,
|
||||
ct,
|
||||
nil,
|
||||
&keyArray,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ptHex := hex.EncodeToString(ptp)
|
||||
if ptHex != test.PT {
|
||||
t.Fatal("decryption failed, expected:", test.PT, "got", ptHex)
|
||||
}
|
||||
}()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user