Sending non-text emails with stunnel and netcat
The initial problem
Today I was putting together an update for my answer on this StackOverflow question, and it occurred to me that (as I’d noted in the edit) it would be possible to create an email that had neither a text/plain part or a text/html part.
While I was fairly confident that this would be true, I thought I should quickly confirm this, and the simplest way that I could think of was to send the email through to the SMTP server using netcat.
However, as I’m using Gmail for my email, the SMTP server is only available via secure connection to either port 465 (for SSL connections) or port 587 (for TLS/STARTTLS connections) as shown here. Therefore, I was going to need some kind of SSL tunnel to allow me to connect in plaintext using netcat, but connect to the SMTP server over SSL. From my days using Slackware consistently, I remembered stunnel, which is conveniently available on macOS via brew.
Cheat sheet
$ brew install stunnel
$ cat << EOF > smtps-gmail.conf
> [smtps-gmail]
> accept = localhost:2525
> client = yes
> connect = smtp.gmail.com:465
> EOF
$ stunnel smtps-gmail.conf
$ nc -c localhost 2525
> EHLO msbit.com.au
> AUTH LOGIN
> <Base64 encoded username>
> <Base64 encoded password>
> MAIL FROM: <tom@msbit.com.au>
> RCPT TO: <tom@msbit.com.au>
> DATA
> Content-Type: application/data; name="data.bin"
> Content-Transfer-Encoding: base64
> <Base64 encoded data>
> .
Making the tunnel
Firstly, we will need to install stunnel, which is as simple as:
$ brew install stunnel
The stunnel configuration file can define multiple services, each with its own local options. In this case, there is only one smtps-gmail, and the configuration defines the endpoint on which to listen:
accept = localhost:2525
the endpoint to connect to:
connect = smtp.gmail.com:465
and the mode in which to operate:
client = yes
which means that the listening connection does not require SSL.
Putting it all together we have:
[smtps-gmail]
accept = localhost:2525
client = yes
connect = smtp.gmail.com:465
We can now start the tunnel by running:
$ stunnel smtps-gmail.conf
NOTE: The listening port is set to 2525 to ensure it can be bound to as an unprivileged user. If I wanted to use the well-known port for SMTP, port 25,
stunnelwould need to be run as a privileged user (e.g. viasudo).
Preparing to send
We need to prepare a few items in advance, as the commands are entered interactively:
- username
- password
- message data
All of these involve Base64 encoding to protect the contents during transmission, as SMTP is traditionally not 8-bit clean.
Format username
To prepare the username and password strings:
$ echo -n 'username' | base64
dXNlcm5hbWU=
Format password
$ echo -n 'password' | base64
cGFzc3dvcmQ=
Generate message body
For the purposes of this, I’m going to generate a random file for sending:
$ dd if=/dev/urandom of=data.bin bs=1024 count=1
$ base64 --break 76 data.bin
ypV6owJCIyb+nm5ayZbeeaVp5Wqfxw+VWce+NE2DDAz+LDWCNbFd05LfyyXCVaclsL65Q26ILiHD
RvzMMyRRPyvWvG8q/R3yQcUykFXvTQ4GmOkpMo4hhcqmmfj9blhXT0gTW5UHIMDCSJdvMc8gaS4i
tvEs5ftjCeTMoX1IXOv0N5hDPwue125ZFkG4WCK4LTd44BIRJyVDdMLh+ycVNmCkFq+l3dCTy34r
w2/c9WPpvzo+w1rvA2ryZcd4MXVBbLc7EBA5V0v7IKrCj6af/GmfqulxPWHCwDdhiOc/v1ctO8Q1
jaorupDkRioF3YnN/6SZDJYYxcc5okLRh8DOmYGPlLLx1gthqXeo6BY1wcf5Rh5JiRf6gSgZ2djL
PWBXt0TC96ndK4VT/pxQvShDx/eIG5QSaziNFXQUkVnXfxyazwZdZRgVxdjwrpkM0MZQixVgMPoS
81ucUlhu99SCd+LPfWbsa9Cloyec6GAmfXwvNaYK57g15fTZRm9wftJnzeqavoePIFK9ldgkdgob
Ek2Cm+lEK+2IVmrYg0Y4zWDsuWX01Wf9SKw+QFro4zdMpFrFCB2GAW8ErZrvWE/mjRlXtY4Ja2MN
OQ6KF8pRuzLehNJJhvQJjKGP2RzTqs/OcNnGULOUiy9SIBEnszzhtjqlkMbuZ+cwEKWmRX+/5Wi9
CK28ZxAMSEuAQbCOEEAcKcrqWJKr8bgrOmp0A41IaiZIKzwqkONinCFOEdRSdRvGpJ67oDplvzB6
gLT2Ij+7F5Sc3Yyg00mXK+Zae7bcsMwYVWcH6b3XYRo0LaNUTlCz4oHFWY4/rhhEhVA0zRZpKKYX
H9C7qInKh1+1TJ3hmNW87jrttFft/ggW0LYoTKZEVEAeJHtICJay6GsFaqG6q/KRCdV1ZxfImMgH
gbhgEEzDq2hTpACgBkRu7SUrcKPSzXgBufbUrZwH4EZRcj+x6PRFM0D9EOT+uPQxK3EBNmTjwwLa
uYpP0hDg84o4e7IkCbBHdNNc1PK1YuPXlKjIyNQd5kOQxJJY8+GVgA5IGTrSWUitCh/yW7U8Px7B
Mw7xMKxAdu4nBptvS8mgc1ueikyRGVPTv3NJ44jO6IBCqce3a0yGXLZ3DNc1p0IR1MKe6ta584Fd
bCw5/3/QowqroFMEORxdq/UY7Flea4FufoVCmAhHOVm/W7s4F/ZnF7sUBH0GV8b5cnLmpn1KPsKK
hA4jqT+KrofjiPreu99qGkv01oxPAPLDGNPU7ahCPETK6bznGMO3Fw8dZW9ElonZywP99P8UQU7q
7QP1KMA8EdB0DDdtLpvQeuHWBv3aBgwRnx1HC44fs6yzCVn9H5/SfDUyWh29dWJZWpRBM7If4Q==
The simplest format for the body of the message specifies the Content-Type, the Content-Transfer-Encoding and provides the encoded file. Given that this is for testing sending an email without parts of type text/plain or text/html, the message body will be as follows:
Content-Type: application/data; name="data.bin"
Content-Transfer-Encoding: base64
ypV6owJCIyb+nm5ayZbeeaVp5Wqfxw+VWce+NE2DDAz+LDWCNbFd05LfyyXCVaclsL65Q26ILiHD
RvzMMyRRPyvWvG8q/R3yQcUykFXvTQ4GmOkpMo4hhcqmmfj9blhXT0gTW5UHIMDCSJdvMc8gaS4i
tvEs5ftjCeTMoX1IXOv0N5hDPwue125ZFkG4WCK4LTd44BIRJyVDdMLh+ycVNmCkFq+l3dCTy34r
w2/c9WPpvzo+w1rvA2ryZcd4MXVBbLc7EBA5V0v7IKrCj6af/GmfqulxPWHCwDdhiOc/v1ctO8Q1
jaorupDkRioF3YnN/6SZDJYYxcc5okLRh8DOmYGPlLLx1gthqXeo6BY1wcf5Rh5JiRf6gSgZ2djL
PWBXt0TC96ndK4VT/pxQvShDx/eIG5QSaziNFXQUkVnXfxyazwZdZRgVxdjwrpkM0MZQixVgMPoS
81ucUlhu99SCd+LPfWbsa9Cloyec6GAmfXwvNaYK57g15fTZRm9wftJnzeqavoePIFK9ldgkdgob
Ek2Cm+lEK+2IVmrYg0Y4zWDsuWX01Wf9SKw+QFro4zdMpFrFCB2GAW8ErZrvWE/mjRlXtY4Ja2MN
OQ6KF8pRuzLehNJJhvQJjKGP2RzTqs/OcNnGULOUiy9SIBEnszzhtjqlkMbuZ+cwEKWmRX+/5Wi9
CK28ZxAMSEuAQbCOEEAcKcrqWJKr8bgrOmp0A41IaiZIKzwqkONinCFOEdRSdRvGpJ67oDplvzB6
gLT2Ij+7F5Sc3Yyg00mXK+Zae7bcsMwYVWcH6b3XYRo0LaNUTlCz4oHFWY4/rhhEhVA0zRZpKKYX
H9C7qInKh1+1TJ3hmNW87jrttFft/ggW0LYoTKZEVEAeJHtICJay6GsFaqG6q/KRCdV1ZxfImMgH
gbhgEEzDq2hTpACgBkRu7SUrcKPSzXgBufbUrZwH4EZRcj+x6PRFM0D9EOT+uPQxK3EBNmTjwwLa
uYpP0hDg84o4e7IkCbBHdNNc1PK1YuPXlKjIyNQd5kOQxJJY8+GVgA5IGTrSWUitCh/yW7U8Px7B
Mw7xMKxAdu4nBptvS8mgc1ueikyRGVPTv3NJ44jO6IBCqce3a0yGXLZ3DNc1p0IR1MKe6ta584Fd
bCw5/3/QowqroFMEORxdq/UY7Flea4FufoVCmAhHOVm/W7s4F/ZnF7sUBH0GV8b5cnLmpn1KPsKK
hA4jqT+KrofjiPreu99qGkv01oxPAPLDGNPU7ahCPETK6bznGMO3Fw8dZW9ElonZywP99P8UQU7q
7QP1KMA8EdB0DDdtLpvQeuHWBv3aBgwRnx1HC44fs6yzCVn9H5/SfDUyWh29dWJZWpRBM7If4Q==
Sending the email
Now that we have the tunnel running and our encoded contents prepared, we can connect through it to the SMTP server by running:
$ nc -c localhost 2525
NOTE: On macOS, the
netcatcommand has the-coption, which sends CRLF as the line-ending. This is important when entering the body of the email, as the end of the data is expected to be exactly<CR><LF>.<CR><LF>.
We are greeted with the service ready message:
220 smtp.gmail.com ESMTP v17-v6sm3241947pfn.177 - gsmtp
From here, we interactively enter the SMTP commands effectively in a script as expected by the server:
EHLO msbit.com.au
250-smtp.gmail.com at your service, [xxx.xxx.xxx.xxx]
250-SIZE 35882577
250-8BITMIME
250-AUTH LOGIN PLAIN XOAUTH2 PLAIN-CLIENTTOKEN OAUTHBEARER XOAUTH
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-CHUNKING
250 SMTPUTF8
AUTH LOGIN
334 VXNlcm5hbWU6
dXNlcm5hbWU=
334 UGFzc3dvcmQ6
cGFzc3dvcmQ=
235 2.7.0 Accepted
MAIL FROM: <tom@msbit.com.au>
250 2.1.0 OK v17-v6sm3241947pfn.177 - gsmtp
RCPT TO: <tom@msbit.com.au>
250 2.1.5 OK v17-v6sm3241947pfn.177 - gsmtp
DATA
354 Go ahead v17-v6sm3241947pfn.177 - gsmtp
Content-Type: application/data; name="data.bin"
Content-Transfer-Encoding: base64
ypV6owJCIyb+nm5ayZbeeaVp5Wqfxw+VWce+NE2DDAz+LDWCNbFd05LfyyXCVaclsL65Q26ILiHD
RvzMMyRRPyvWvG8q/R3yQcUykFXvTQ4GmOkpMo4hhcqmmfj9blhXT0gTW5UHIMDCSJdvMc8gaS4i
tvEs5ftjCeTMoX1IXOv0N5hDPwue125ZFkG4WCK4LTd44BIRJyVDdMLh+ycVNmCkFq+l3dCTy34r
w2/c9WPpvzo+w1rvA2ryZcd4MXVBbLc7EBA5V0v7IKrCj6af/GmfqulxPWHCwDdhiOc/v1ctO8Q1
jaorupDkRioF3YnN/6SZDJYYxcc5okLRh8DOmYGPlLLx1gthqXeo6BY1wcf5Rh5JiRf6gSgZ2djL
PWBXt0TC96ndK4VT/pxQvShDx/eIG5QSaziNFXQUkVnXfxyazwZdZRgVxdjwrpkM0MZQixVgMPoS
81ucUlhu99SCd+LPfWbsa9Cloyec6GAmfXwvNaYK57g15fTZRm9wftJnzeqavoePIFK9ldgkdgob
Ek2Cm+lEK+2IVmrYg0Y4zWDsuWX01Wf9SKw+QFro4zdMpFrFCB2GAW8ErZrvWE/mjRlXtY4Ja2MN
OQ6KF8pRuzLehNJJhvQJjKGP2RzTqs/OcNnGULOUiy9SIBEnszzhtjqlkMbuZ+cwEKWmRX+/5Wi9
CK28ZxAMSEuAQbCOEEAcKcrqWJKr8bgrOmp0A41IaiZIKzwqkONinCFOEdRSdRvGpJ67oDplvzB6
gLT2Ij+7F5Sc3Yyg00mXK+Zae7bcsMwYVWcH6b3XYRo0LaNUTlCz4oHFWY4/rhhEhVA0zRZpKKYX
H9C7qInKh1+1TJ3hmNW87jrttFft/ggW0LYoTKZEVEAeJHtICJay6GsFaqG6q/KRCdV1ZxfImMgH
gbhgEEzDq2hTpACgBkRu7SUrcKPSzXgBufbUrZwH4EZRcj+x6PRFM0D9EOT+uPQxK3EBNmTjwwLa
uYpP0hDg84o4e7IkCbBHdNNc1PK1YuPXlKjIyNQd5kOQxJJY8+GVgA5IGTrSWUitCh/yW7U8Px7B
Mw7xMKxAdu4nBptvS8mgc1ueikyRGVPTv3NJ44jO6IBCqce3a0yGXLZ3DNc1p0IR1MKe6ta584Fd
bCw5/3/QowqroFMEORxdq/UY7Flea4FufoVCmAhHOVm/W7s4F/ZnF7sUBH0GV8b5cnLmpn1KPsKK
hA4jqT+KrofjiPreu99qGkv01oxPAPLDGNPU7ahCPETK6bznGMO3Fw8dZW9ElonZywP99P8UQU7q
7QP1KMA8EdB0DDdtLpvQeuHWBv3aBgwRnx1HC44fs6yzCVn9H5/SfDUyWh29dWJZWpRBM7If4Q==
.
250 2.0.0 OK 1533692605 v17-v6sm3241947pfn.177 - gsmtp
QUIT
221 2.0.0 closing connection v17-v6sm3241947pfn.177 - gsmtp
Note the final . line to end the DATA portion.
The email should now have been sent!
Confirming the output
When checking my inbox, there is a new email from me, with no subject, or body, but an attachment. The full email message is as shown below:
Bcc: tom@msbit.com.au
Return-Path: <tom@msbit.com.au>
Received: from msbit.com.au (xxx.xxx.xxx.xxx.xxx. [xxx.xxx.xxx.xxx])
by smtp.gmail.com with ESMTPSA id v17-v6sm3241947pfn.177.2018.08.07.18.43.09
for <tom@msbit.com.au>
(version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);
Tue, 07 Aug 2018 18:43:25 -0700 (PDT)
Message-ID: <5b6a4abd.1c69fb81.319ec.8e62@mx.google.com>
Date: Tue, 07 Aug 2018 18:43:25 -0700 (PDT)
From: tom@msbit.com.au
Content-Type: application/data; name="data.bin"
Content-Transfer-Encoding: base64
ypV6owJCIyb+nm5ayZbeeaVp5Wqfxw+VWce+NE2DDAz+LDWCNbFd05LfyyXCVaclsL65Q26ILiHD
RvzMMyRRPyvWvG8q/R3yQcUykFXvTQ4GmOkpMo4hhcqmmfj9blhXT0gTW5UHIMDCSJdvMc8gaS4i
tvEs5ftjCeTMoX1IXOv0N5hDPwue125ZFkG4WCK4LTd44BIRJyVDdMLh+ycVNmCkFq+l3dCTy34r
w2/c9WPpvzo+w1rvA2ryZcd4MXVBbLc7EBA5V0v7IKrCj6af/GmfqulxPWHCwDdhiOc/v1ctO8Q1
jaorupDkRioF3YnN/6SZDJYYxcc5okLRh8DOmYGPlLLx1gthqXeo6BY1wcf5Rh5JiRf6gSgZ2djL
PWBXt0TC96ndK4VT/pxQvShDx/eIG5QSaziNFXQUkVnXfxyazwZdZRgVxdjwrpkM0MZQixVgMPoS
81ucUlhu99SCd+LPfWbsa9Cloyec6GAmfXwvNaYK57g15fTZRm9wftJnzeqavoePIFK9ldgkdgob
Ek2Cm+lEK+2IVmrYg0Y4zWDsuWX01Wf9SKw+QFro4zdMpFrFCB2GAW8ErZrvWE/mjRlXtY4Ja2MN
OQ6KF8pRuzLehNJJhvQJjKGP2RzTqs/OcNnGULOUiy9SIBEnszzhtjqlkMbuZ+cwEKWmRX+/5Wi9
CK28ZxAMSEuAQbCOEEAcKcrqWJKr8bgrOmp0A41IaiZIKzwqkONinCFOEdRSdRvGpJ67oDplvzB6
gLT2Ij+7F5Sc3Yyg00mXK+Zae7bcsMwYVWcH6b3XYRo0LaNUTlCz4oHFWY4/rhhEhVA0zRZpKKYX
H9C7qInKh1+1TJ3hmNW87jrttFft/ggW0LYoTKZEVEAeJHtICJay6GsFaqG6q/KRCdV1ZxfImMgH
gbhgEEzDq2hTpACgBkRu7SUrcKPSzXgBufbUrZwH4EZRcj+x6PRFM0D9EOT+uPQxK3EBNmTjwwLa
uYpP0hDg84o4e7IkCbBHdNNc1PK1YuPXlKjIyNQd5kOQxJJY8+GVgA5IGTrSWUitCh/yW7U8Px7B
Mw7xMKxAdu4nBptvS8mgc1ueikyRGVPTv3NJ44jO6IBCqce3a0yGXLZ3DNc1p0IR1MKe6ta584Fd
bCw5/3/QowqroFMEORxdq/UY7Flea4FufoVCmAhHOVm/W7s4F/ZnF7sUBH0GV8b5cnLmpn1KPsKK
hA4jqT+KrofjiPreu99qGkv01oxPAPLDGNPU7ahCPETK6bznGMO3Fw8dZW9ElonZywP99P8UQU7q
7QP1KMA8EdB0DDdtLpvQeuHWBv3aBgwRnx1HC44fs6yzCVn9H5/SfDUyWh29dWJZWpRBM7If4Q==
As we can see, the email has been delivered, the content is exactly the same as what was sent, and there is no mention of a text/plain or text/html component. As a final test, we can download the data.bin attachment and compare the SHA hashes:
$ shasum data.bin
e54b3d70b1368829d5db4c9ea77f2f667adf5b59 data.bin
$ shasum ~/Downloads/data.bin
e54b3d70b1368829d5db4c9ea77f2f667adf5b59 /Users/tom/Downloads/data.bin
which also confirm that the file has been successfully sent.