💾 Archived View for gemi.dev › gemini-mailing-list › 000871.gmi captured on 2024-08-19 at 02:23:27. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2023-12-28)

-=-=-=-=-=-=-

[tech] tls user_canceled issue with Java server + Go client

1. Alan (gemini (a) bunburya.eu)

Recently I decided to have a go at writing a basic Gemini server in Kotlin 
and have found some rather unusual behaviour.

When using the Amfora browser, I was unable to view "20" responses, 
getting instead the following error: "/Issuing creating page: remote 
error: tls: user//canceled/". Unusually, every other response is displayed fine.

Other clients written in Go also seem to be affected by this: Solderpunk's 
demo client (https://tildegit.org/solderpunk/gemini-demo-3) has the same 
issue, for example. Non-Go clients (Castor, Kristall, Lagrange, gmni) don't.

My Kotlin code gets an instance of javax.net.ssl.SSLContext using the TLS 
protocol, loads a KeyStore file (in JKS or PKCS12 format) and creates an 
SSLServerSocket instance that it uses to listen for connections. I haven't 
uploaded my code anywhere, but the approach is similar to that taken in 
Earl (https://github.com/mrletourneau/EarlServer), which has the same issue for me.

 From searching around for the error message and looking at Java logs, it 
seems that under TLSv1.3, Java (I tested on openjdk8 and openjdk11) sends 
/alert(user_canceled)/ to the client just prior to /alert(notify_close)/ 
(to notify it of the intention to close the TLS connection). And it looks 
like this/user_canceled/ is interpreted as an error by Go, whereas other 
libraries just ignore it. I tested the Jemini server 
(https://github.com/warmuuh/jemini) which uses Jetty and the problem 
doesn't arise there (it doesn't seem to send /user_canceled/); I guess 
Jetty has its own implementation of SSL sockets?

Has anyone else had similar problems trying to make requests to a 
JVM-based Gemini server using a Go-based client (or in other 
circumstances)? Or can others replicate this behaviour using (for example) 
Earl + Amfora? Assuming the problem isn't just me, I'm not sure if it's 
better described as an issue with Go or Java - it seems strange that Java 
would send "user_canceled", but equally Go probably probably shouldn't 
treat it as an error. Regardless, I wanted to mention it in case it trips 
anyone else up. I guess Go clients could manually intercept and gracefully 
handle the alert, but I suspect the preferred answer will simply be not to 
use servers that send it.

I'm fairly new to the world of TLS so apologies if I'm getting something 
completely wrong.

Link to individual message.

2. Gary Johnson (lambdatronic (a) disroot.org)

Alan <gemini@bunburya.eu> writes:

> When using the Amfora browser, I was unable to view "20" responses,
> getting instead the following error: "/Issuing creating page: remote 
> error: tls: user//canceled/". Unusually, every other response is
> displayed fine.

Hi Alan,

I'm the author of Space-Age, a Gemini server written in Clojure.

=> https://gitlab.com/lambdatronic/space-age/

The tls_user_canceled issue you are running into is coming from the Java
side. It also mystified me last year when I was writing my server since
I was constantly getting that error in elpher (the Emacs Gemini client)
whenever I tried to browse pages from my capsule.

You have to force your SSLSocket to send the tls_close_notify alert to
the client before closing the socket. This is exceptionally poorly
documented in the JSSE Reference Guide, but I eventually figured out the
solution through trial and error.

All you need to do is to make sure you call socket.shutdownOutput()
immediately before calling socket.close().

Here's my function for writing a Gemini response back to the client for reference:

 ```
(defn- write-socket! [^SSLSocket socket {:keys [status meta body]}]
  (doto (io/writer socket)
    (.write (str status " " meta "\r\n"))
    (.flush))
  (when body
    (with-open [in-stream (io/input-stream (if (string? body)
                                             (.getBytes ^String body)
                                             body))]
      (let [out-stream (io/output-stream socket)]
        (.transferTo in-stream out-stream)
        (.flush out-stream))))
  (.shutdownOutput socket))
 ```

This is called from another function that then calls socket.close() to
terminate the connection.

Here is my full Gemini server implementation for further reference:

=> https://gitlab.com/lambdatronic/space-age/-/blob/master/src/space_age/server.clj

Happy hacking!
  Gary

-- 
GPG Key ID: 7BC158ED
Use `gpg --search-keys lambdatronic' to find me
Protect yourself from surveillance: https://emailselfdefense.fsf.org
=======================================================================
()  ascii ribbon campaign - against html e-mail
/\  www.asciiribbon.org   - against proprietary attachments

Why is HTML email a security nightmare? See https://useplaintext.email/

Please avoid sending me MS-Office attachments.
See http://www.gnu.org/philosophy/no-word-attachments.html

Link to individual message.

3. Alan (gemini (a) bunburya.eu)

Hi Gary,

That solved it, thanks! A simple fix but you're right, I couldn't find 
anything about it. Good to know there is a solution.

Thanks,

Alan

On 18/04/2021 01:04, Gary Johnson wrote:
> Alan<gemini@bunburya.eu>  writes:
> 
>> When using the Amfora browser, I was unable to view "20" responses,
>> getting instead the following error: "/Issuing creating page: remote
>> error: tls: user//canceled/". Unusually, every other response is
>> displayed fine.
> Hi Alan,
> 
> I'm the author of Space-Age, a Gemini server written in Clojure.
> 
> =>https://gitlab.com/lambdatronic/space-age/
> 
> The tls_user_canceled issue you are running into is coming from the Java
> side. It also mystified me last year when I was writing my server since
> I was constantly getting that error in elpher (the Emacs Gemini client)
> whenever I tried to browse pages from my capsule.
> 
> You have to force your SSLSocket to send the tls_close_notify alert to
> the client before closing the socket. This is exceptionally poorly
> documented in the JSSE Reference Guide, but I eventually figured out the
> solution through trial and error.
> 
> All you need to do is to make sure you call socket.shutdownOutput()
> immediately before calling socket.close().
> 
> Here's my function for writing a Gemini response back to the client for reference:
> 
> ```
> (defn- write-socket! [^SSLSocket socket {:keys [status meta body]}]
>    (doto (io/writer socket)
>      (.write (str status " " meta "\r\n"))
>      (.flush))
>    (when body
>      (with-open [in-stream (io/input-stream (if (string? body)
>                                               (.getBytes ^String body)
>                                               body))]
>        (let [out-stream (io/output-stream socket)]
>          (.transferTo in-stream out-stream)
>          (.flush out-stream))))
>    (.shutdownOutput socket))
> ```
> 
> This is called from another function that then calls socket.close() to
> terminate the connection.
> 
> Here is my full Gemini server implementation for further reference:
> 
> =>https://gitlab.com/lambdatronic/space-age/-/blob/master/src/space_age/server.clj
> 
> Happy hacking!
>    Gary
>

Link to individual message.

4. Bill Havanki (desu (a) deszaras.xyz)

My thanks to Gary as well. I found that my own Java server implementation 
had the same problem.

My server gets the socket’s input and output streams in a 
try-with-resources block, so they are closed when the block exits. 
Unfortunately, when either of those streams are closed, Java closes the socket as well!

So, I fixed the user_canceled problem by making a thin wrapper object 
around the socket output stream. When that stream wrapper is closed, it 
calls shutdownOutput on the socket before closing the original stream 
(which then closes the socket). Works nicely.

https://github.com/bhavanki/doppio/blob/a4f8802d6e0dbeb066fa71f8c79b66d8616
b3cf1/src/main/java/com/havanki/doppio/RequestHandler.java#L555

Bill

> On Apr 18, 2021, at 10:41 AM, Alan <gemini@bunburya.eu> wrote:
>
> Hi Gary,
>
> That solved it, thanks! A simple fix but you're right, I couldn't find
> anything about it. Good to know there is a solution.
>
> Thanks,
>
> Alan
>
> On 18/04/2021 01:04, Gary Johnson wrote:
>
>> Alan<gemini@bunburya.eu> writes:
>>
>>> When using the Amfora browser, I was unable to view "20" responses,
>>> getting instead the following error: "/Issuing creating page: remote
>>> error: tls: user//canceled/". Unusually, every other response is
>>> displayed fine.
>>
>> Hi Alan,
>>
>> I'm the author of Space-Age, a Gemini server written in Clojure.
>>
>> =>https://gitlab.com/lambdatronic/space-age/
>>
>> The tls_user_canceled issue you are running into is coming from the Java
>> side. It also mystified me last year when I was writing my server since
>> I was constantly getting that error in elpher (the Emacs Gemini client)
>> whenever I tried to browse pages from my capsule.
>>
>> You have to force your SSLSocket to send the tls_close_notify alert to
>> the client before closing the socket. This is exceptionally poorly
>> documented in the JSSE Reference Guide, but I eventually figured out the
>> solution through trial and error.
>>
>> All you need to do is to make sure you call socket.shutdownOutput()
>> immediately before calling socket.close().
>>
>> Here's my function for writing a Gemini response back to the client for reference:
>>
>> ```
>> (defn- write-socket! [^SSLSocket socket {:keys [status meta body]}]
>> (doto (io/writer socket)
>> (.write (str status " " meta "\r\n"))
>> (.flush))
>> (when body
>> (with-open [in-stream (io/input-stream (if (string? body)
>> (.getBytes ^String body)
>> body))]
>> (let [out-stream (io/output-stream socket)]
>> (.transferTo in-stream out-stream)
>> (.flush out-stream))))
>> (.shutdownOutput socket))
>> ```
>>
>> This is called from another function that then calls socket.close() to
>> terminate the connection.
>>
>> Here is my full Gemini server implementation for further reference:
>>
>> =>https://gitlab.com/lambdatronic/space-age/-/blob/master/src/space_age/server.clj
>>
>> Happy hacking!
>> Gary

Link to individual message.

---

Previous Thread: [tech] IPv6 addresses in URLs

Next Thread: [ANN] New version of Agunua with the recursive downloader