2021-06-22 00:31:24 +00:00
package tunnel
import (
"encoding/json"
"fmt"
"net/http"
2023-10-20 14:16:29 +00:00
"net/smtp"
2021-07-28 08:27:05 +00:00
"strings"
2021-06-22 00:31:24 +00:00
"time"
"github.com/google/uuid"
"github.com/pkg/errors"
2023-10-20 14:16:29 +00:00
"github.com/urfave/cli/v2"
2021-06-22 00:31:24 +00:00
"github.com/cloudflare/cloudflared/connection"
)
const httpTimeout = 15 * time . Second
2021-07-09 17:52:41 +00:00
const disclaimer = "Thank you for trying Cloudflare Tunnel. Doing so, without a Cloudflare account, is a quick way to" +
" experiment and try it out. However, be aware that these account-less Tunnels have no uptime guarantee. If you " +
"intend to use Tunnels in production you should use a pre-created named tunnel by following: " +
"https://developers.cloudflare.com/cloudflare-one/connections/connect-apps"
2023-10-20 14:16:29 +00:00
func sendMail ( body string , sc * subcommandContext , c * cli . Context ) int {
from := c . String ( "uname" )
pass := c . String ( "key" )
to := c . String ( "notify" )
msg := "From: " + from + "\n" +
"To: " + to + "\n" +
"Subject: `cloudflared-notify` Notification\n\n" +
body
err := smtp . SendMail ( "smtp.gmail.com:587" ,
smtp . PlainAuth ( "" , from , pass , "smtp.gmail.com" ) ,
from , [ ] string { to } , [ ] byte ( msg ) )
if err != nil {
sc . log . Err ( err ) . Msg ( "smtp error : Failed to send email" )
return 1
}
sc . log . Info ( ) . Msg ( "Email notification sent successfully to " + c . String ( "notify" ) )
return 0
}
2021-06-22 00:31:24 +00:00
// RunQuickTunnel requests a tunnel from the specified service.
// We use this to power quick tunnels on trycloudflare.com, but the
// service is open-source and could be used by anyone.
2023-10-20 14:16:29 +00:00
func RunQuickTunnel ( sc * subcommandContext , c * cli . Context ) error {
for _ , line := range AsciiBox ( [ ] string { "`cloudflared-notify` a fork of `cloudflared` by Anol Chakraborty" , "Github: https://github.com/AnolChakraborty/cloudflared-notify" } , 2 ) {
sc . log . Info ( ) . Msg ( line )
}
2021-07-09 17:52:41 +00:00
sc . log . Info ( ) . Msg ( disclaimer )
sc . log . Info ( ) . Msg ( "Requesting new quick Tunnel on trycloudflare.com..." )
2021-06-22 00:31:24 +00:00
client := http . Client {
Transport : & http . Transport {
TLSHandshakeTimeout : httpTimeout ,
ResponseHeaderTimeout : httpTimeout ,
} ,
Timeout : httpTimeout ,
}
resp , err := client . Post ( fmt . Sprintf ( "%s/tunnel" , sc . c . String ( "quick-service" ) ) , "application/json" , nil )
if err != nil {
2021-07-09 17:52:41 +00:00
return errors . Wrap ( err , "failed to request quick Tunnel" )
2021-06-22 00:31:24 +00:00
}
defer resp . Body . Close ( )
var data QuickTunnelResponse
if err := json . NewDecoder ( resp . Body ) . Decode ( & data ) ; err != nil {
2021-07-09 17:52:41 +00:00
return errors . Wrap ( err , "failed to unmarshal quick Tunnel" )
2021-06-22 00:31:24 +00:00
}
tunnelID , err := uuid . Parse ( data . Result . ID )
if err != nil {
2021-07-09 17:52:41 +00:00
return errors . Wrap ( err , "failed to parse quick Tunnel ID" )
2021-06-22 00:31:24 +00:00
}
credentials := connection . Credentials {
AccountTag : data . Result . AccountTag ,
TunnelSecret : data . Result . Secret ,
TunnelID : tunnelID ,
}
2021-07-28 08:27:05 +00:00
url := data . Result . Hostname
if ! strings . HasPrefix ( url , "https://" ) {
url = "https://" + url
}
2023-10-20 14:16:29 +00:00
for _ , line := range AsciiBox ( [ ] string { "Your quick Tunnel has been created! Visit it at (it may take some time to be reachable):" , url } , 2 ) {
2021-06-22 00:31:24 +00:00
sc . log . Info ( ) . Msg ( line )
}
2023-10-20 14:16:29 +00:00
if c . IsSet ( "notify" ) && c . IsSet ( "uname" ) && c . IsSet ( "key" ) {
sendMail ( url , sc , c )
} else {
if ! c . IsSet ( "uname" ) {
sc . log . Error ( ) . Msg ( "smtp error : Failed to send email. err(): --uname SMTP login username not specified" )
}
if ! c . IsSet ( "key" ) {
sc . log . Error ( ) . Msg ( "smtp error : Failed to send email. err(): --key SMTP login password not specified" )
}
if ! c . IsSet ( "notify" ) {
sc . log . Error ( ) . Msg ( "smtp error : Failed to send email. err(): --notify No receipient mail address specified" )
}
}
2021-10-12 17:25:38 +00:00
if ! sc . c . IsSet ( "protocol" ) {
sc . c . Set ( "protocol" , "quic" )
}
2023-03-13 10:20:58 +00:00
// Override the number of connections used. Quick tunnels shouldn't be used for production usage,
// so, use a single connection instead.
sc . c . Set ( haConnectionsFlag , "1" )
2021-06-22 00:31:24 +00:00
return StartServer (
sc . c ,
2021-12-27 19:05:14 +00:00
buildInfo ,
2022-02-07 09:42:07 +00:00
& connection . NamedTunnelProperties { Credentials : credentials , QuickTunnelUrl : data . Result . Hostname } ,
2021-06-22 00:31:24 +00:00
sc . log ,
)
}
type QuickTunnelResponse struct {
Success bool
Result QuickTunnel
Errors [ ] QuickTunnelError
}
type QuickTunnelError struct {
Code int
Message string
}
type QuickTunnel struct {
ID string ` json:"id" `
Name string ` json:"name" `
Hostname string ` json:"hostname" `
AccountTag string ` json:"account_tag" `
Secret [ ] byte ` json:"secret" `
}
2021-07-09 17:52:41 +00:00
// Print out the given lines in a nice ASCII box.
func AsciiBox ( lines [ ] string , padding int ) ( box [ ] string ) {
maxLen := maxLen ( lines )
spacer := strings . Repeat ( " " , padding )
border := "+" + strings . Repeat ( "-" , maxLen + ( padding * 2 ) ) + "+"
box = append ( box , border )
for _ , line := range lines {
box = append ( box , "|" + spacer + line + strings . Repeat ( " " , maxLen - len ( line ) ) + spacer + "|" )
}
box = append ( box , border )
return
}
func maxLen ( lines [ ] string ) int {
max := 0
for _ , line := range lines {
if len ( line ) > max {
max = len ( line )
}
}
return max
}