Because the order of fields can be significant. It's very relevant for syscalls, and is observable via the reflect package; it'd be strange if the field order was arbitrarily changed (and might change further between releases).
I assume the thinking was that this is pretty easy to optimise if you care, and if it's on by default there'd then have to be some opt-out which there isn't a good mechanism for.
> struct field alignment matches C (even without cgo)
The spec defines alignment for numeric types, but that's about it. There is nothing in the spec about struct layout. That is implementation dependent. If you are relying on a particular implementation, you are decidedly in unsafe territory.
> so any change to the default would break a lot of code.
The compiler can disable optimization on cgo calls automatically and most other places where it matters are via the standard library, so it might not be as much as you think. And if you still have a case where it matters, that is what this is for: https://pkg.go.dev/structs#HostLayout
That's good to know. I'm not making use of this assumption, but the purego package (came from ebitengine) does. It looks like they're aware of HostLayout [1] but I'm not sure how many other people have gotten the memo (HostLayout didn't exist before Go 1.23).
binary.Read/Write already uses reflect as far as I can tell, so it wouldn't matter in that case. There is an optimization for numeric values in there, which may be what you are thinking of? But they are specified in the spec and that's not what we're talking about anyway (and if an optimization really had to go, for whatever reason, it wouldn't be the end of the world).
Basically, binary.Read/binary.Write are capable of reading/writing struct values. The worry would be that, if the Go compiler reordered fields under the hood, the order may differ between writing the data and reading it back (especially across different versions of Go).
However, because these functions use reflection, they likely wouldn't be affected. While the in-memory layout of the struct fields might get reordered, presumably the reflection order would match the declaration order. Indeed, there is already an Offset field on the reflect.StructField type, and there is no statement anywhere I can find that such offsets must increase monotonically.
So, the fields would remain in declaration order when inspected with reflection, but their offsets could jump around within the struct, yet well behaved reflection-based code should be agnostic to this change.
This is very unfortunate, since most structs are never going to be passed to C, yet end up paying the tax anyway. They really should have made it opt-in.
That requires a way to opt out tho, because there are situations where you need a specific field ordering, so now the langage needs to provide way to tune struct compilation behaviour.
Like the answer to all "Why doesn't Go have X?" questions: Lack of manpower. There has been some work done to support it, but is far from complete. Open source doesn't mean open willingness to contribute, unfortunately. Especially when you're not the cool kid on the block.
The tribal knowledge seems to be that you shouldn't do TCP-based hole punching because it's harder than UDP. The author acknowledges this:
> You can do NAT traversal with TCP, but it adds another layer of complexity to an already quite complex problem, and may even require kernel customizations depending on how deep you want to go.
However, I only see marginally added complexity (given the already complex UDP flows). IMO this complexity doesn't justify discarding TCP hole punching altogether. In the article you could replace raw UDP packets to initiate a connection with TCP SYN packets plus support for "simultaneous open" [0].
This is especially true if networks block UDP traffic which is also acknowledged:
> For example, we’ve observed that the UC Berkeley guest Wi-Fi blocks all outbound UDP except for DNS traffic.
My point is that many articles gloss over TCP hole punching with the excuse of being harder than UDP while I would argue that it's almost equally feasible with marginal added complexity.
The existence of stateful firewalls, and the fact that most NAT filters are EDF rather than EIF means that simultaneous open (send) is necessary even for UDP.
Hence the added complexity of doing a simultaneous open via TCP is fairly minor. The main complication is communicating the public mapping, and coordinating the "simultaneous" punch/open. However that is generally needed for UDP anyway...
One possible added complexity with TCP is one has to perform real connect() calls, rather than fake up the TCP SYN packet. That is becase some firewalls pay attention to the sequence numbers.
Yeah, I've gotten somewhat annoyed by the name of 'NAT traversal' for these methods. It seems to make some people think that cutting out NAT will lead to a beautiful world of universal P2P connections. But really, these methods are needed for traversing between any two networks behind stateful firewalls, which will pose a barrier to P2P indefinitely.
Also, wouldn't it be easier for stateful firewalls to block simultaneous TCP open (intentionally or not)? With UDP, the sender's firewall must create a connection as soon as it sends off the first packet, even if that packet bounces off the other firewall: the timing doesn't have to be particularly tight. But with TCP, the firewall might plausibly wait until the handshake is complete before allowing incoming packets, and it might only allow the 3-way SYN/SYN-ACK/ACK instead of the simultaneous SYN/SYN/ACK/ACK.
> But really, these methods are needed for traversing between any two networks behind stateful firewalls, which will pose a barrier to P2P indefinitely.
That's true. The actual problem are symmetric NATs where every peer sees a different port number. This makes traditional NAT-traversal impossible and you have to resort to port guessing/scanning. See for example https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&d...
People honestly thought for a while that devices behind a NAT were secured unless ports were specifically routed to them, hence the term "nat hole punching" was coined.
You're probably right that it makes less sense from today's perspective
Doesn't make sense. NAT hole punching requires you to execute on the target inside the NAT.
If you are able to do that whatever security you got from NAT has been breached even before NAT hole punching enters the conversation.
NAT will block unsolicited incoming connections, that is a great boon for security but obviously not a silver bullet for all network related security issues nor outgoing connections. That has never been a trope.
> Doesn't make sense. NAT hole punching requires you to execute on the target inside the NAT.
Why doesn't it make sense to you? From my perspective the idea was that the NAT protects your devices - and your device is now punching a hole into this protection, making it vulnerable to the world wide web
This circumventing doesn't have to be done by a malicious actor, it just comes at the added risk of becoming "targetable" from the Internet
Because it is the same thing as opening an outgoing connection but with more steps. The only thing it allows is to connect to someone else that is also behind NAT.
By this logic, a firewall has no bearing on security either. It just drops packets / makes devices unadressable unless a route has been allowed/ a port has been opened
If a NAT made a device unreachable from the Internet, it'd be more secure then a device that's reachable.
You could theoretically leave management ports unsecured etc, because there is no danger - they can't be reached after all.
It just turned out that techniques such as NAT hole punching were developed, which made this rationale invalid. Because your devices could still become reachable.
Yes, this specific technique requires a an active part on both ends, but this active part can be something completely innocent, such as an activeX applet in Internet explorer 6.
Or a Freeware they installed, and now opens up the whole network, not just the device is installed on.
This is all from the very beginnings of the Internet, things weren't as explored back then, and that's why NATs were considered a security feature back then.
Modern malware is obviously much more advanced so we intuitively know why it was never really providing any safety. Hackers were mostly doing things for bragging rights, cryptolockers weren't a thing, the current default malware setup of having command hosts that compromised hosts check for new commands to execute weren't even thought of. In these times, NAT looked like a security feature, because it kinda looks like it.
I think this is a really good point. As someone who has implemented TCP hole punching myself and now has a very good implementation for it I will say that obviously a major benefit of using TCP is you don't have to subsequently roll a poorman's TCP on-top of UDP once the hole is open. The other issue with TCP hole punching though is it looks very similar to a SYN flood compared to UDP packets. This may mean lower success rates for some networks. Though in practice I haven't seen much filtering so far.
TCP hole punching is very fun. The way I do it is to use multiple NTP readings to compute a "clock skew" -- how far off the system clock is from NTP. Then the initiator sets a future meeting time that is relative to NTP. It honestly gets quite accurate. It even works for TCP hole punching between sockets on the same interface which is crazy if you think about it.
The reason I wanted to support this strange, local-based punching mode is if it works that efficiently to be able to succeed in host-based punching then likely it will be fast enough to work on the LAN and Internet, too. My code is Python and my very first attempt at this was eye opening to say the least. Due to how timing-sensitive TCP hole punching is I was having failures from using Python with old-school self-managed sockets. I was using threading and a poormans event loop (based on my C socket experience)... which is ah... just not the way to do it in Python.
The only way I could get that code to work was to ensure the Python process had a high priority so other processes on the system didn't deprioritize it and introduce lag between the punching attempts. That is how time-critical the code is (with an inefficient implementation.) My current implementation now uses a process pool that each has its own event loop to manage punching. I create a list of tasks that are distributed over time. Each task simply opens a connection that is reused from the same socket. I determined this code was the best approach (in Python anyway) after testing it on every major OS.
You are right about TCP and UDP hole punching difficulty being similar. The main difficulty to both is the NAT prediction step. I haven't written code yet for symmetric NAT bypass but I am starting to see how I'd integrate it (or possibly write a new plugin for it.)
I did just think of another drawback for TCP vs UDP punching that I think puts a major point in UDP's favour. It may have been touched on others already. But TCP would require the router to record connection state. This is bad because the table for routers is very small and some of these punching techniques are quite aggressive. Like the algorithm that tries to bypass symmetric NATs. If you're opening hundreds of TCP connections its possible you might even DoS the router. For UDP its plausible optimizations for state management would make it less likely that your punching would render the whole router inoperable. This is only speculation though.
> If you're opening hundreds of TCP connections its possible you might even DoS the router.
This was sometimes an issue for underpowered home/SOHO routers in the mid-2000s, but most modern routers have enough memory to support decently sized connection-tracking tables.
In any case, both TCP and UDP require connection tracking; there's no inherent advantage to UDP.
I had the exact same thought for years but never came around experimenting with it. I also hoped that one could eventually hear that something is off.
I think this can happen by either recognising the "rhythm" in which sounds appear and/or recognising different tones.
As a first step, my idea was to write a logger that plays different beep sounds for different log levels. That way you could mostly identify the “rhythm” because I guess most log messages would have the same severity. However, to a tiny degree also by the pitch of the sound.
Then as a second step I thought of mapping the log message to a scale of sounds by e.g., hashing the message. This obviously would only work if there’s no dynamic content in the message.
I’m currently working on a new version of pcp [0]. Based on croc, magic-wormhole and the likes it doesn’t require a relay. It uses the IPFS DHT as a discovery point to connect two machines. Haven’t touched the currently released code in two years but the new hole punching capabilities of libp2p show promising result so I’m working on a new version.
Here is my feedback about the pcp's main GitHub page, if you don't mind:
- Move "Motivation", "Project Status", "How Does It Work?" somewhere else
- Add "What is It?" and "Encryption" sections and place them just before the "Usage" section. Keep them reasonable small, the smaller the better
I believe that it will help the project to have a much better resonance with the target audience.
Does this run a mini IPFS node on the sender and relayer and rely instead on the IPFS network being up to propagate the DHT changes through the network?
It’s just running a very lightweight libp2p host that speaks the DHT protocol. It’s basically just taking the bare minimum part of Kubo (IPFS) that’s necessary to interact with the DHT.
It’s then relying on the rest of the IPFS network to propagate the record for discovering the sender and receiver.
It’s gone from the ipfs.io gateway but the CID is still available in the network. Take your own IPFS node (e.g. companion or Brave’s built-in) and you should be able to resolve it
during December 2022, we are running a measurement campaign to investigate decentralized NAT hole punching success rates using the libp2p DCUtR protocol [0]. Ubiquitous peer-to-peer connectivity is still a big challenge. If successful, NAT Hole Punching can be a game-changer for decentralised applications and networks!
For that we are searching for participants who would run a lean client on their machines that performs hole punches with other peers and then reports back the results to our server. We explained the measurement methodology in this video [1] and the linked repository above.
Running such a client certainly has privacy implications which are documented here [2]. Most importantly, we record public IP addresses, successful NAT port mappings, and the login router page (to draw conclusions about which routers work better than others).
Optionally, you can also sign up here [3] and provide additional information about your personal network and receive a personal API key so that we can link your data to your information. Obviously, this has stronger privacy implications - but this is totally optional.
The most frictionless way to participate is to head to the releases page [4] and download a client that suits your platform and needs. No sign-up required.
A few months ago I built a CLI frontend for Hashicorps shamir secret sharing implementation in Go. You can find it here: https://github.com/dennis-tra/shamir
It combines the two separate commands in the article into one.
If you’re happy with the feature set of ~Photoshop CS2 you may want to check out Photopea [0]. It runs in your browser, has a similar UI to Photoshop and (for me) has all bells an whistles. It’s also open source [1].
Ad supported. I think they got an ad free paid version. Been using it for years and I believe it covers over 90% of non professional photoshop use cases.
gh api notifications -X PUT -F last_read_at=2025-10-06T00:00:00Z
Just change the date to today. I also got that line from a gh issue somewhere - maybe it was the same issue that you’re referring to.