SwiftNIO: Application Protocol Negotiation Result Not Correct

I use SwiftNIO to capture traffics in Man-in-the-middle, the client connect with MiTM, and the MiTM acts as a fake server. During the TLS handshake phase, the negotiation result is different from Charles. Specifically, the ApplicationProtocolNegotiationHandler's result is h2, but Charles is http/1.1, and Charles got the right one.

Can someone help me to locate the cause?

Don't offer h2.

ALPN is a negotiated protocol. The client offers a list of protocols it supports, and then the server picks from among them. In NIO we do this with a simple priority list. In this case, the client is clearly offering h2,http/1.1 as the supported protocols. You are offering h2,http/1.1 as well, so we pick h2. If you don't want to support h2, don't offer it.

@lukasa
Thank you for your answer, I had a flash of inspiration the night before yesterday and also thought that h2 should not be offered. I have solved this problem now, Thank you.

In the client-to-MiTM phase, I shouldn't offer h2, because MiTM does not know whether the real Server supports h2. But in the MiTM-to-server phase, I should offer h2, because I connect to the real server, the real server knows whether h2 is supported or not.

If you don't offer h2 to the client, you shouldn't offer it to the server either: it's best to force the same protocol along the entire pipeline.

The best thing to do is to offer the same protocols that the client offers to the server, and reconfigure around them. SwiftNIO does not really enable this use-case today: we don't give you any way to choose what protocols you offer based on the client's offer. We could, though.

I know what you mean, that's what I did at first. But I had a thorny problem, the client offers [http/1.1, h2] to MiTM, and the result of negotiation between the client and MiTM was h2, then MiTM offers [http/1.1, h2] to the real server, and the result of negotiation between MiTM and the real server was http/1.1, so the client connected to MiTM using h2 related handlers and MiTM connected to the real server using http/1.1 related handlers, when the http/1.1 response data returned from the real server, I don't know how to convert the http/1.1 data to h2 format that the client required (Maybe I know).

Now, what I do is that the client negotiates directly with the MiTM using http/1.1, and the MiTM negotiates with the real server using http/1.1 and h2. If the real server only supports http/1.1, there is no problem with the whole process. If the server supports h2, Then I just need to convert the http/1.1 request from the client into h2 and send it to the corresponding server. Similarly, when the h2 response comes back, I directly convert it into http/1.1 and send it to the corresponding client. I have implemented all this steps and it works very well.

1 Like